Load Libraries

Load all relevant R libraries.

# Load Libraries 
library("RPostgres")
library("ggplot2")
library("tidyverse")
library("dbplyr")
library("lubridate")
library("data.table")
library("dtplyr")
library("ggpubr")
library("muStat")
library("mvnormtest")
library("knitr")
library("kableExtra")

Connect to Relational Database

# load the PostgreSQL driver
drv <- dbDriver("Postgres")

# create a connection to the postgres database
# set the search path to the mimiciii schema
con <- dbConnect(drv, dbname = "mimic",
                 host = "localhost", port = 5432,
                 user = "mousaghannam")
dbSendQuery(con, 'set search_path to mimiciii')
<PqResult>
  SQL  set search_path to mimiciii
  ROWS Fetched: 0 [complete]
       Changed: 0

Show a list of all the tables in the relational database.

# show a list of tables
dbListTables(con)
Closing open result set, cancelling previous query
 [1] "chartevents_8"      "admissions"         "callout"            "caregivers"         "chartevents_1"      "chartevents_2"     
 [7] "chartevents_3"      "chartevents_4"      "chartevents_5"      "chartevents_6"      "chartevents_7"      "chartevents_9"     
[13] "chartevents_10"     "chartevents_11"     "icustays"           "inputevents_cv"     "inputevents_mv"     "labevents"         
[19] "microbiologyevents" "chartevents_12"     "chartevents_13"     "chartevents_14"     "chartevents"        "chartevents_15"    
[25] "chartevents_16"     "chartevents_17"     "cptevents"          "datetimeevents"     "diagnoses_icd"      "drgcodes"          
[31] "d_cpt"              "d_icd_diagnoses"    "prescriptions"      "services"           "d_icd_procedures"   "d_items"           
[37] "d_labitems"         "noteevents"         "outputevents"       "patients"           "procedureevents_mv" "procedures_icd"    
[43] "transfers"         

Function for more easily pulling a given table.

This is a memory conservative approaching for performing SQL queries on DB. We can pull tables in memory and do certain analyses on them, without actually having to save a given object into the R environment.

#Memory conserative approaching for performing SQL queries on DB 
#(Rewrite this function to handle multiple inputs )
tbl_mimic <- function(table) {
  table <- as.character(substitute(table))
  tbl(con, dbplyr::in_schema("mimiciii", table))
}

Create dictionary of diagnosis codes

This “dictionary” will contain all of the diagnoses events in the database associated with a given hospital admission id (hadm_id), along with a description of the icd9 code.

#Pull dictionary for diagnoses
d_icd_diagnoses <- tbl_mimic(d_icd_diagnoses) %>%
  select(icd9_code, short_title)

diagnoses_icd <-tbl_mimic(diagnoses_icd) %>%
  select(hadm_id, icd9_code, seq_num)

#ICD-9 Codes + their descriptions
icd9_dict <- diagnoses_icd %>%
  inner_join(d_icd_diagnoses, by = "icd9_code")

icd9_dict

Load Electrolyte Lab Measurements

Determine the ids corresponding to lab values for each electrolyte

Potassium

Saved all of the lab_ids into R environment with collect(), for filtering in the next step.

d_labitems <- tbl_mimic(d_labitems) #Pull dictionary for lab items into memory 

d_labitems %>%
  filter(str_detect(tolower(label), "potassium")) %>%
  collect() -> lab_idsK

lab_idsK

Magnesium

d_labitems %>%
  filter(str_detect(tolower(label), "magnesium")) %>%
  collect() -> lab_idsMg

lab_idsMg
NA

Calcium

d_labitems %>%
  filter(str_detect(tolower(label), "calcium")) %>%
  collect() -> lab_idsCa

lab_idsCa
NA

Phosphate

d_labitems %>%
  filter(str_detect(tolower(label), "phosphate")) %>%
  collect() -> lab_idsPh

lab_idsPh
NA

Filter them

We are choosing to include the 1st and 4th row, which are strictly blood lab value draws for potassium. Do the same for the other electrolytes.

#Drugs to include for potassium 
itemidsK <- lab_idsK$itemid[c(1,4)]

#Drugs to include for magnesium 
itemidsMg <- lab_idsMg$itemid[c(1)]

#Drugs to include for Calcium 
itemidsCa <- lab_idsCa$itemid[c(4)]

#Drugs to include for Phosphate
itemidsPh <- lab_idsPh$itemid[c(2)]

Pull table containing all lab measurements.

We are now taking the table labevents, which contains all the lab events in the database, and selecting the rows associated with the two blood potassium lab values itemids that we chose above.

labevents <- tbl_mimic(labevents)

labevents %>% 
  filter(itemid %in% itemidsK)  %>%
  inner_join(select(tbl_mimic(d_labitems),-loinc_code, -row_id ), by = "itemid") %>%
  select(subject_id, hadm_id, itemid, charttime, valuenum, valueuom, label, flag, fluid, category) -> labEventsK

labEventsK

labevents %>% 
  filter(itemid %in% itemidsMg)  %>%
  inner_join(select(tbl_mimic(d_labitems),-loinc_code, -row_id ), by = "itemid") %>%
  select(subject_id, hadm_id, itemid, charttime, valuenum, valueuom, label, flag, fluid, category) -> labEventsMg

labEventsMg

labevents %>% 
  filter(itemid %in% itemidsCa)  %>%
  inner_join(select(tbl_mimic(d_labitems),-loinc_code, -row_id ), by = "itemid") %>%
  select(subject_id, hadm_id, itemid, charttime, valuenum, valueuom, label, flag, fluid, category) -> labEventsCa

labEventsCa

labevents %>% 
  filter(itemid %in% itemidsPh)  %>%
  inner_join(select(tbl_mimic(d_labitems),-loinc_code, -row_id ), by = "itemid") %>%
  select(subject_id, hadm_id, itemid, charttime, valuenum, valueuom, label, flag, fluid, category) -> labEventsPh

labEventsPh


# print(paste(" There are ",pull(numLabKevents[1,1]), " total potassium lab values that were extracted from the labevents table." ))

Define repletion events within the metaview database

Potassium

The d_items table in mimics contains a dictionary for all of the events associated with both the metaview and careview database. We are only going to focus on the metaview database at this point. We then want to filter this table by the label description for items that are related to potassium.

d_items <- tbl_mimic(d_items) #Pull dictionary for all lab events into memory 

d_items %>%
  filter(label %ilike% "%potassium%" | label %ilike% "%kcl%") %>%
  filter(str_detect(tolower(dbsource), "meta")) %>%
  arrange(label) -> k_items_MV

k_items_MV 
NA

Although there are events sourced from either metaview or careview, these two hospital databases are actually linked to 3 different tables in the mimics database: inputevents_mv, inputevents_cv or chartevents. Since we are examining only the metavision database, it will only link to inputevents_mv and chartevents. We would like to select for only the events that link to inputevents_mv.

k_items_MV %>% 
  filter(linksto=="inputevents_mv") %>% collect() -> k_items_MV_IM

k_items_MV_IM

If you look at the above table, the first row has a KCL (Bolus) with unit mL, while the 5th row has Potassium Chloride with units in mEq. According to the mimics iii website:

“Metavision records IO data using two tables: RANGESIGNALS and ORDERENTRY. These tables do not appear in MIMIC-III as they have been merged to form the > INPUTEVENTS_MV table. RANGESIGNALS contains recorded data elements which last for a fixed period of time. Furthermore, the RANGESIGNALS table recorded > information for each component of the drug separately. For example, for a norepinephrine administration there would be two components: a main order > component (norepinephrine) and a solution component (NaCl).” 1

Therefore, we will only be selecting repletions based off of the “main order component”, and ignoring the solution. We will select for the 4th and 5th rows, which correspond to Potassiume Acetate and Potassium Chloride.

k_items_MV_IM %>% slice(-6,-3, -2,-1) -> k_items_MV_IM #ONLY KEEPING THE REPLETIONS, THE ADDITIVE AMOUNTS, NOT THE BOLUS 
k_items_MV_IM

k_items_MV_IM %>% pull(itemid) -> k_items_MV_IM_vector 

Magnesium

d_items <- tbl_mimic(d_items) #Pull dictionary for all lab events into memory 

d_items %>%
  filter(label %ilike% "%magnesium%" | label %ilike% "%mg%") %>%
  filter(str_detect(tolower(dbsource), "meta")) %>%
  arrange(label) -> mg_items_MV

mg_items_MV %>% 
  filter(linksto=="inputevents_mv") %>% collect() -> mg_items_MV_IM

mg_items_MV_IM

mg_items_MV_IM %>% slice(1) -> mg_items_MV_IM #ONLY KEEPING THE REPLETIONS, THE ADDITIVE AMOUNTS, NOT THE BOLUS 
mg_items_MV_IM

mg_items_MV_IM %>% pull(itemid) -> mg_items_MV_IM_vector 

Calcium

d_items %>%
  filter(label %ilike% "%calcium%" ) %>%
  filter(str_detect(tolower(dbsource), "meta")) %>%
  arrange(label) -> ca_items_MV

ca_items_MV 

ca_items_MV %>% 
  filter(linksto=="inputevents_mv") %>% collect() -> ca_items_MV_IM

ca_items_MV_IM

ca_items_MV_IM %>% slice(1) -> ca_items_MV_IM #ONLY KEEPING THE REPLETIONS, THE ADDITIVE AMOUNTS, NOT THE BOLUS 
ca_items_MV_IM

ca_items_MV_IM %>% pull(itemid) -> ca_items_MV_IM_vector 

Phosphate

d_items %>%
  filter(label %ilike% "%phosphate%"  | label %ilike% "na%") %>%
  filter(str_detect(tolower(dbsource), "meta")) %>%
  arrange(label) -> ph_items_MV

ph_items_MV 

ph_items_MV %>% 
  filter(linksto=="inputevents_mv") %>% collect() -> ph_items_MV_IM

ph_items_MV_IM

ph_items_MV_IM %>% slice(1,8) -> ph_items_MV_IM #ONLY KEEPING THE REPLETIONS, THE ADDITIVE AMOUNTS, NOT THE BOLUS 
ph_items_MV_IM

ph_items_MV_IM %>% pull(itemid) -> ph_items_MV_IM_vector 

Pull repletion events from metaview databse

Potassium

We will now use the filtered ids for the two additive solutions above to pull electrolyte repletions from the inputevents_mv table.

inputevents_mv <- tbl_mimic(inputevents_mv)

inputevents_mv %>%
  filter(itemid %in% k_items_MV_IM_vector) %>%
  select(subject_id, hadm_id,icustay_id, linkorderid, orderid, itemid, starttime, endtime, amount, amountuom, rate, rateuom, statusdescription, ordercomponenttypedescription) %>%
  filter(!statusdescription == "Rewritten") %>%
  rename(itemid.repletion = itemid) -> repEventsK

repEventsK

Determine the amount of potassium repletions:

repEventsK %>% count() %>% collect() -> numKRepletions

print(paste(" There are ",pull(numKRepletions[1,1]), " total potassium repletions that were extracted from the inputevents_mv table." ))
[1] " There are  66429  total potassium repletions that were extracted from the inputevents_mv table."

Magnesium

inputevents_mv %>%
  filter(itemid %in% mg_items_MV_IM_vector) %>%
  select(subject_id, hadm_id,icustay_id, linkorderid, orderid, itemid, starttime, endtime, amount, amountuom, rate, rateuom, statusdescription, ordercomponenttypedescription) %>%
  filter(!statusdescription == "Rewritten") %>%
  rename(itemid.repletion = itemid) -> repEventsMg

repEventsMg

Calcium

inputevents_mv %>%
  filter(itemid %in% ca_items_MV_IM_vector) %>%
  select(subject_id, hadm_id,icustay_id, linkorderid, orderid, itemid, starttime, endtime, amount, amountuom, rate, rateuom, statusdescription, ordercomponenttypedescription) %>%
  filter(!statusdescription == "Rewritten") %>%
  rename(itemid.repletion = itemid) -> repEventsCa

repEventsCa

Phosphate

inputevents_mv %>%
  filter(itemid %in% ph_items_MV_IM_vector) %>%
  select(subject_id, hadm_id,icustay_id, linkorderid, orderid, itemid, starttime, endtime, amount, amountuom, rate, rateuom, statusdescription, ordercomponenttypedescription) %>%
  filter(!statusdescription == "Rewritten") %>%
  rename(itemid.repletion = itemid) -> repEventsPh

repEventsPh

Join tables containing repletion events and lab events

Both the tables containing the lab draws and the repletion events have been extracted. We can now join them to begin analyzing the relationship between them.

In addition to joining the tables, we have renamed the column corresponding to the moment the fluid collection for the lab value was recorded, charttime, as charttime.lab for clarity.

Find recent repletions

We want to flag all repletion events that occurred either 24 hours before or 24 hours after a given lab value. We then filter the table, storing all of the repletions occurring BEFORE a given lab value in one table, and all of the ones occurring AFTER a given lab value in another.

Analysis and Visualization

We are going to do analysis here on the dataset containing repletions. We are making this distinction, as later on, we will do analysis on non-repletions.

Analysis on Repletions

Table

In this table we are just doing some summary analysis on the dataset.

postExclusionsK_Repleted %>%
  group_by( preVsPost) %>%
  summarize(n = n(), mean = mean(valuenum, na.rm = TRUE), standard_deviation = sd(valuenum, na.rm = T)) 

Extract the most recent repletion, prior and after a given lab value.

We have a table that contains all of the repletion events that occur either 24 hours prior or after a given lab value. We then group the rows by each subject and hospital id, as well as the time of fluid acquisition for the lab value. Then, we create a column that generates a flag if the lab value that is chosen is the minimum starttime for all the possible starttimes. Because there are multiple lab values, as well as multiple repletion events, for a given hadm_id the table outputted has all the possible permutations. We want to select the most recent repletion AFTER a lab value, so we choose the repletion with the starttime equal to the minimum starttime for a given lab value.

#Potassium lab event PRE-repletions 
allRepLabEvents_k %>%
  filter(isRecentPre) %>% 
  group_by(subject_id, hadm_id,charttime.lab) %>%
  mutate(isMostRecentRepletion = starttime == min(starttime)) %>%
  filter(isMostRecentRepletion) %>%
  ungroup() %>% 
  group_by(subject_id, hadm_id, starttime) %>%
  mutate(isMostRecentLabEvent = charttime.lab == max(charttime.lab)) %>%
  filter(isMostRecentLabEvent)  %>% distinct() -> pre_k_lab_repletions_MV_new

pre_k_lab_repletions_MV_new

For the post-repletion lab values, we are now doing the same thing, but instead finding the maximum endtime prior to a given lab value, to select the most recent repletion prior to a given lab value.

#Potassium lab event POST-repletions 
allRepLabEvents_k %>%
  filter(isRecentPost) %>% 
  group_by(subject_id, hadm_id,charttime.lab) %>%
  mutate(isMostRecentRepletion = endtime == max(endtime)) %>%
  filter(isMostRecentRepletion) %>%
  ungroup() %>% 
  group_by(subject_id, hadm_id, endtime) %>%
  mutate(isMostRecentLabEvent = charttime.lab == min(charttime.lab)) %>%
  filter(isMostRecentLabEvent)  %>% distinct() -> post_k_lab_repletions_MV_new

post_k_lab_repletions_MV_new

Doing it on the combined dataset

allEs_pre_post_repletions_MV_new %>%
   select(linkorderid, Electrolyte) %>%
  distinct()%>%
  group_by(Electrolyte) %>%
  summarize(n = n())
Adding missing grouping variables: `subject_id`, `hadm_id`, `starttime`
`summarise()` ungrouping output (override with `.groups` argument)

We then combine all of the values from the “pre-repletion” and “post-repletion” tables, into one table. We create a new column preVsPost to indicate if a given event is either before or after a lab value. Conceivably, the same repletion could correspond to a

#Combine them into one dataset 
k_pre_post_repletions_MV_new <- bind_rows(list(pre_repletion = pre_k_lab_repletions_MV_new, post_repletion = post_k_lab_repletions_MV_new), .id = "preVsPost")
k_pre_post_repletions_MV_new %>% distinct()

#This gives the amount of repletions events with the same linkorder id. Anything over 2 might need to be exlcuded, as this should have been broken up. It reflects the inherent error in EHRs it seems like. 
k_pre_post_repletions_MV_new  %>%
  group_by(linkorderid) %>%
  summarize(observations = n()) %>%
  arrange(desc(observations)) %>%
  count(observations)
`summarise()` ungrouping output (override with `.groups` argument)
#Filters all of the rows where there is a post and a pre-repletion value (hopefully lol) 
k_pre_post_repletions_MV_new %>%
  group_by(linkorderid) %>% 
  filter(n() == 1) %>%
  ungroup()%>% 
  group_by(preVsPost) %>%
  summarize( mean = mean(valuenum, na.rm = T), obs = n(), sd = sd(valuenum, na.rm = T))
`summarise()` ungrouping output (override with `.groups` argument)
k_pre_post_repletions_MV_new %>%
  group_by(linkorderid) %>% 
  filter(n() == 2) %>%
  ungroup()%>% 
  group_by(preVsPost) %>%
  summarize( mean = mean(valuenum, na.rm = T), obs = n(), sd = sd(valuenum, na.rm = T))
`summarise()` ungrouping output (override with `.groups` argument)
  
#This gives the frequency for the amount of unique values 
k_pre_post_repletions_MV_new  %>%
  group_by(charttime.lab) %>%
  summarize(observations = n()) %>%
  arrange(desc(observations)) %>%
  count(observations)
`summarise()` ungrouping output (override with `.groups` argument)

Magnesium

allRepLabEvents_mg %>%
  filter(isRecentPre) %>% 
  group_by(subject_id, hadm_id,charttime.lab) %>%
  mutate(isMostRecentRepletion = starttime == min(starttime)) %>%
  filter(isMostRecentRepletion) %>%
  ungroup() %>% 
  group_by(subject_id, hadm_id, starttime) %>%
  mutate(isMostRecentLabEvent = charttime.lab == max(charttime.lab)) %>%
  filter(isMostRecentLabEvent)  %>% distinct() -> pre_mg_lab_repletions_MV_new

pre_mg_lab_repletions_MV_new

#Magnesium lab event POST-repletions 
allRepLabEvents_mg %>%
  filter(isRecentPost) %>% 
  group_by(subject_id, hadm_id,charttime.lab) %>%
  mutate(isMostRecentRepletion = endtime == max(endtime)) %>%
  filter(isMostRecentRepletion) %>%
  ungroup() %>% 
  group_by(subject_id, hadm_id, endtime) %>%
  mutate(isMostRecentLabEvent = charttime.lab == min(charttime.lab)) %>%
  filter(isMostRecentLabEvent)  %>% distinct() -> post_mg_lab_repletions_MV_new

post_mg_lab_repletions_MV_new

#Combine them into one dataset 
mg_pre_post_repletions_MV_new <- bind_rows(list(pre_repletion = pre_mg_lab_repletions_MV_new, post_repletion = post_mg_lab_repletions_MV_new), .id = "preVsPost")
mg_pre_post_repletions_MV_new %>% distinct()

Calcium

#Calcium lab event PRE-repletions 
allRepLabEvents_ca %>%
  filter(isRecentPre) %>% 
  group_by(subject_id, hadm_id,charttime.lab) %>%
  mutate(isMostRecentRepletion = starttime == min(starttime)) %>%
  filter(isMostRecentRepletion) %>%
  ungroup() %>% 
  group_by(subject_id, hadm_id, starttime) %>%
  mutate(isMostRecentLabEvent = charttime.lab == max(charttime.lab)) %>%
  filter(isMostRecentLabEvent)  %>% distinct() -> pre_ca_lab_repletions_MV_new

pre_ca_lab_repletions_MV_new

#Calcium lab event POST-repletions 
allRepLabEvents_ca %>%
  filter(isRecentPost) %>% 
  group_by(subject_id, hadm_id,charttime.lab) %>%
  mutate(isMostRecentRepletion = endtime == max(endtime)) %>%
  filter(isMostRecentRepletion) %>%
  ungroup() %>% 
  group_by(subject_id, hadm_id, endtime) %>%
  mutate(isMostRecentLabEvent = charttime.lab == min(charttime.lab)) %>%
  filter(isMostRecentLabEvent)  %>% distinct() -> post_ca_lab_repletions_MV_new

post_ca_lab_repletions_MV_new

#Combine them into one dataset 
ca_pre_post_repletions_MV_new <- bind_rows(list(pre_repletion = pre_ca_lab_repletions_MV_new, post_repletion = post_ca_lab_repletions_MV_new), .id = "preVsPost")
ca_pre_post_repletions_MV_new %>% distinct()
NA

Phosphate

#Phosphate lab event PRE-repletions 
allRepLabEvents_ph %>%
  filter(isRecentPre) %>% 
  group_by(subject_id, hadm_id,charttime.lab) %>%
  mutate(isMostRecentRepletion = starttime == min(starttime)) %>%
  filter(isMostRecentRepletion) %>%
  ungroup() %>% 
  group_by(subject_id, hadm_id, starttime) %>%
  mutate(isMostRecentLabEvent = charttime.lab == max(charttime.lab)) %>%
  filter(isMostRecentLabEvent)  %>% distinct() -> pre_ph_lab_repletions_MV_new

pre_ph_lab_repletions_MV_new

#Phosphate lab event POST-repletions 
allRepLabEvents_ph %>%
  filter(isRecentPost) %>% 
  group_by(subject_id, hadm_id,charttime.lab) %>%
  mutate(isMostRecentRepletion = endtime == max(endtime)) %>%
  filter(isMostRecentRepletion) %>%
  ungroup() %>% 
  group_by(subject_id, hadm_id, endtime) %>%
  mutate(isMostRecentLabEvent = charttime.lab == min(charttime.lab)) %>%
  filter(isMostRecentLabEvent)  %>% distinct() -> post_ph_lab_repletions_MV_new

post_ph_lab_repletions_MV_new

#Combine them into one dataset 
ph_pre_post_repletions_MV_new <- bind_rows(list(pre_repletion = pre_ph_lab_repletions_MV_new, post_repletion = post_ph_lab_repletions_MV_new), .id = "preVsPost")
ph_pre_post_repletions_MV_new %>% distinct()
NA

#Combine all datasets into one We’re going to make one giant dataset across all of the electrolytes.

all_lytes_pre_post_repletions_MV_new %>%
  select(linkorderid, Electrolyte) %>%
  distinct()%>%
  group_by(Electrolyte) %>%
  summarize(n = n())
Adding missing grouping variables: `subject_id`, `hadm_id`, `starttime`
`summarise()` ungrouping output (override with `.groups` argument)

Exclusions

We now want to exclude all the rows based on certain criteria.

We have a table called hadm_id_table which contains all of the hadm_ids of patients with diagnoses that would confound our results (kidney disease, etc). (WILL ADD HOW I CREATED THIS TABLE LATER).

hadm_id_table

Examining amount of exclusions

First bubble of schemtic

#The amount of distinct repletions across each electrolyte 
all_repletions_MV_new_AEs %>%
  ungroup()%>%
  select(linkorderid, Electrolyte) %>%
  distinct() %>%
  group_by(Electrolyte) %>%
  summarize(n = n()) %>% 
  arrange(desc(n)) %>%
  kable()
`summarise()` ungrouping output (override with `.groups` argument)
Electrolyte n
potassium 63365
magnesium 24601
calcium 20206
phosphate 1339

Third bubble of schematic: Those that have been excluded pre and post 24 hrs, and also the most recent lab value before and after each repletion

allEs_pre_post_repletions_MV_new %>% #Amount of DISTINCT Repletion Events left 
  ungroup()%>%
  anti_join(hadm_id_table, by = "hadm_id") %>% 
  select(linkorderid, Electrolyte) %>%
  distinct() %>%
  group_by(Electrolyte) %>%
  summarize(n = n()) %>% 
  arrange(desc(n)) %>%
  kable()
`summarise()` ungrouping output (override with `.groups` argument)
Electrolyte n
potassium 17755
magnesium 8008
calcium 4055
phosphate 348

As a result, we could simply do a semi-join to determine how many people in each group were excluded. Note, we are doing the anti-join on the table with the most recent repletion before and after a given lab result, within a 24 hour time range. We are not doing it on the set of ALL possible repletion and or lab result events.

hadm_id_table %>%
  semi_join(all_lytes_pre_post_repletions_MV_new, by = "hadm_id") %>% 
  select(hadm_id, .id) %>%
  group_by(.id) %>%
  summarize(n = n()) %>% 
  arrange(desc(n)) %>%
  kable()

## Number of hospital visits for potassium
hadm_id_table %>%
  # semi_join(k_pre_post_repletions_MV_new, by = "hadm_id") %>%
  inner_join(k_pre_post_repletions_MV_new, by = "hadm_id") %>%
  select(hadm_id, .id) %>%
  group_by(.id) %>%
  summarize(n = n()) %>% 
  arrange(desc(n)) %>%
  kable()

k_pre_post_repletions_MV_new %>%
  ungroup()%>%
  semi_join(hadm_id_table, by = "hadm_id") %>% 
  select(icustay_id, orderid, .id) %>%
  group_by(.id) %>%
  summarize(n = n()) %>% 
  arrange(desc(n)) %>%
  kable()

And here is the resultant dataset after actually excluding this time, using an anti-join instead.

k_pre_post_repletions_MV_new %>%
  anti_join(hadm_id_table, by = "hadm_id") -> postExclusionsK_Repleted

postExclusionsK_Repleted

###Another Exclusions

k_pre_post_repletions_MV_new %>%
  semi_join(hadm_id_table, by = "hadm_id")

hadm_id_table %>%
  semi_join(k_pre_post_repletions_MV_new, by = "hadm_id")

Non Repletion Events

We will now determine the set of lab values for which there was no immediate repletion afterward.

Extract non-repleted values within 24 hours

In this section, we have started by filtering all the repletions prior and after a lab value, just like for the analysis on the repletions. However, this time, if there are multiple lab values that occur before a given repletion (or after), we are taking the ones after the first one.

#All electrolyte lab event PRE-repletions 
all_electrolyte_repLabEvents %>%
  filter(isRecentPre) %>% 
  group_by(subject_id, hadm_id,charttime.lab, Electrolyte) %>%
  mutate(isMostRecentRepletion = starttime == min(starttime)) %>%
  filter(isMostRecentRepletion) %>%
  ungroup() %>% 
  group_by(subject_id, hadm_id, starttime, Electrolyte) %>%
  mutate(isMostRecentLabEvent = charttime.lab == max(charttime.lab)) %>%
  filter(!isMostRecentLabEvent)  %>% distinct() -> nonRepletLabsPre_allEs

#All electrolyte lab event POST-repletions 
all_electrolyte_repLabEvents %>%
  filter(isRecentPost) %>% 
  group_by(subject_id, hadm_id,charttime.lab, Electrolyte) %>%
  mutate(isMostRecentRepletion = endtime == max(endtime)) %>%
  filter(isMostRecentRepletion) %>%
  ungroup() %>% 
  group_by(subject_id, hadm_id, endtime, Electrolyte) %>%
  mutate(isMostRecentLabEvent = charttime.lab == min(charttime.lab)) %>%
  filter(!isMostRecentLabEvent)  %>% distinct() -> nonRepletLabsPost_allEs

allNonRepletions_allEs <- bind_rows(list(pre_repletion = nonRepletLabsPre_allEs, post_repletion = nonRepletLabsPost_allEs), .id = "preVsPost")

Table

allNonRepletions_allEs %>%
  group_by(preVsPost, Electrolyte) %>%
  summarize(n = n(), mean = mean(valuenum, na.rm = TRUE), standard_deviation = sd(valuenum, na.rm = T))
`summarise()` regrouping output by 'preVsPost' (override with `.groups` argument)

Visualizations

Exclusions

We now want to exclude all the rows based on certain criteria.

We have a table called hadm_id_table which contains all of the hadm_ids of patients with diagnoses that would confound our results (kidney disease, etc). (WILL ADD HOW I CREATED THIS TABLE LATER).

hadm_id_table

As a result, we could simply do a semi-join to determine how many people in each group were excluded. Note, we are doing the anti-join on the table with the most recent repletion before and after a given lab result, within a 24 hour time range. We are not doing it on the set of ALL possible repletion and or lab result events.

hadm_id_table %>%
  semi_join(all_lytes_pre_post_repletions_MV_new, by = "hadm_id") %>% 
  select(hadm_id, .id) %>%
  group_by(.id) %>%
  summarize(n = n()) %>% 
  arrange(desc(n)) %>%
  kable()
`summarise()` ungrouping output (override with `.groups` argument)
.id n
renal-codes 8127
afib-codes 4013
aki-codes 3914
chf-codes 3332
cad-codes 1262
esrd-codes 735
prbc 613
ckd-codes 535
dialysis-codes 219
rhabdo-codes 180
paralyzed-codes 164
parathyroid-codes 66
sarcoid-codes 58
pediatrics 20
burn-codes 15
nutrit-def 7

And here is the resultant dataset after actually excluding this time, using an anti-join instead.

k_pre_post_repletions_MV_new %>%
  anti_join(hadm_id_table, by = "hadm_id") -> postExclusionsK_Repleted

postExclusionsK_Repleted

###Another Exclusions

k_pre_post_repletions_MV_new %>%
  semi_join(hadm_id_table, by = "hadm_id")

hadm_id_table %>%
  semi_join(k_pre_post_repletions_MV_new, by = "hadm_id")

Analysis and Visualization

We are going to do analysis here on the dataset containing repletions. We are making this distinction, as later on, we will do analysis on non-repletions.

Analysis on Repletions

Table

In this table we are just doing some summary analysis on the dataset.

postExclusionsK_Repleted %>%
  group_by( preVsPost) %>%
  summarize(n = n(), mean = mean(valuenum, na.rm = TRUE), standard_deviation = sd(valuenum, na.rm = T)) 

Histogram

Histogram of Pre and Post Repletions

ggpar(gghistogram(data = postExclusionsK_Repleted, x="valuenum", fill = "preVsPost",  add="mean", palette = c("#00AFBB", "#E7B800"),add_density = FALSE, bins = 40, gggtheme = theme_pubr(), xlab = "Lab Value", title = "Pre Vs Post Repletion Lab Values - Potassium", ylab = "Repletions")
      , xlim = c(2,6))

ggplot(postExclusionsK_Repleted, aes(x = valuenum,fill=preVsPost)) +
  geom_histogram(alpha=0.6, position="identity",bins=40, color="black") + scale_x_continuous(limits = c(2,6) ) + theme_pubr() + xlab("Potassium Value") + ylab("Number of Occurrences") + scale_fill_discrete(name = "", labels = c("Post repletion K", "Pre repletion K")) -> h1_k

h1_k

Pie Chart


  postExclusionsK_Repleted %>%
     mutate(repletionRange = case_when(valuenum < 3.5 & flag == "abnormal" ~ "below normal",
                                      valuenum > 3.5 & flag == "abnormal" ~ "above range",TRUE ~ "within range")) -> postExclusionsK_Repleted

postExclusionsK_Repleted %>%
  filter(repletionRange == "above range") %>% 
  group_by(valuenum) %>%
  summarize(observations = n()) %>% 
   arrange(valuenum) 


postExclusion_freq <- postExclusionsK_Repleted %>%
  group_by(repletionRange) %>%
  summarize(observations = n()) %>%
  mutate(percentage = observations / sum(observations) * 100) 

ggplot(postExclusion_freq, aes("", percentage, fill = repletionRange)) + 
  geom_bar(stat = "identity", color = "white", size = 1) +
  coord_polar(theta = "y") +
  scale_fill_manual(values =c( "#B3B3B3","#27408B","#EE7621"),name = " ",
                    labels = c( "Above 5.2","Below 3.4", "3.4-5.2" )
                    ) +
  theme_void() + theme(legend.position="top")  -> pie_chart_k

pie_chart_k

Non - Repletion Analysis

allNonRepletions <- bind_rows(list(pre_repletion = nonRepletLabsPre, post_repletion = nonRepletLabsPost), .id = "preVsPost")

#Run Exclusions
allNonRepletions %>%
  anti_join(hadm_id_table, by = "hadm_id") -> postexclusion_nonRepletions

postexclusion_nonRepletions

Table

postexclusion_nonRepletions %>%
  group_by( preVsPost) %>%
  summarize(n = n(), mean = mean(valuenum, na.rm = TRUE), standard_deviation = sd(valuenum, na.rm = T)) 

Combining the repleted vs NonRepleted pre lab values for comparison

repletedVsNonRepleted_PreV <- bind_rows(list(repleted = postExclusionsK_Repleted, nonRepleted = postexclusion_nonRepletions), .id = "repleteVsNon") %>% filter(preVsPost == "pre_repletion")
repletedVsNonRepleted_PreV %>% distinct()

Table

#Table
repletedVsNonRepleted_PreV %>%
  group_by(repleteVsNon,flag) %>%
  summarize(observations = n(),mean = mean(valuenum, na.rm = T), std_dev = sd(valuenum, na.rm=T)) %>%
  mutate(percentage = observations / sum(observations) * 100) 

Visualization

#Visualizing the non-repleted vs repleted lab values 
ggpar(gghistogram(data = repletedVsNonRepleted_PreV, x="valuenum", fill = "flag", add="mean",position = "identity", palette = c("#00AFBB", "#E7B800"),add_density = FALSE, bins = 100, gggtheme = theme_pubr(), xlab = "Lab Value", title = "Pre Vs Post Repletion Lab Values - Potassium", ylab = "Repletions", facet.by = "repleteVsNon")
      , xlim = c(2,6))

Question: How can there be histogram bars that are “abnormal” on top of bars that are white? Are they just not granular enough to differentiate them? I think there are errors in how the data was flagged. Should look more into this. #### Looking at the outliers

Repleted above the threshold.
repletedVsNonRepleted_PreV %>%
  filter(repleteVsNon == "repleted") %>%
  filter(flag == "abnormal" & valuenum > 4) %>%
  ungroup()%>%
  summarize(observations = n(),mean = mean(valuenum, na.rm = T), std_dev = sd(valuenum, na.rm=T)) 
Didn’t replete Below the threshold.
Table
postexclusion_nonRepletions %>%
  filter(flag == "abnormal" & valuenum < 4) %>%
  ungroup() %>%
  summarize(observations = n(),mean = mean(valuenum, na.rm = T), std_dev = sd(valuenum, na.rm=T)) 
Visualization of not repletiong below the threshold
postexclusion_nonRepletions %>%
  filter(valuenum < 3.5 & flag == "abnormal") %>%
  ggplot(aes(x = valuenum)) +
  geom_histogram(alpha=0.6, position="identity",bins=14, color="black") + scale_x_continuous(limits = c(1,3.5)) + theme_pubr()

###Amount of people that replete (vs Non-Replete) across lab value

repletedVsNonRepleted_PreV %>%
  group_by(valuenum, repleteVsNon) %>%
  summarize(observations = n() ) %>%
  ggplot(aes(fill=repleteVsNon, y=observations, x=valuenum)) + 
    geom_bar(position="fill", stat="identity") + scale_x_continuous()  + theme_pubr()

Time of Day Analysis

Examining the time of day for each repletion and lab value.

repletedVsNonRepleted_PreV %>%
  mutate(RepletionHour = hour(starttime), LabHour = hour(charttime.lab)) %>%
  gghistogram(x = c("LabHour","RepletionHour"), bins = 24, palette  = "uchicago", alpha = 0.5, merge = T, xlab = "Hours", ylab = "Number of Occurences")

repletedVsNonRepleted_PreV %>%
  ungroup() %>%
  mutate(LabHour = hour(charttime.lab) + 4, RepletionHour = hour(starttime)) %>%
  pivot_longer(cols = c(LabHour,RepletionHour), values_to="Hour", names_to="WhichHour") -> yy

ordered(yy$WhichHour)
yy$WhichHour <- factor(yy$WhichHour, ordered = FALSE)
yy$WhichHour <- relevel(yy$WhichHour,"RepletionHour")
ggplot(yy, aes(x = Hour,fill=WhichHour)) + geom_histogram(position="identity",bins = 24,alpha=0.6) + 
  scale_x_continuous(breaks=seq(0,24,2))  + theme_pubr()

ggplot(yy, aes(x = Hour,fill=WhichHour)) +
  geom_histogram(alpha=0.4, position="identity",bins=24, color="black") + scale_x_continuous(breaks=seq(0,24,2)) + facet_wrap(vars(repleteVsNon))

Pre-Repletion vs Non-Repleted Lab Values

Below, we have the time of day for lab value draws that occur for lab values that were repleted, and those that did not directly lead to a repletion.

It seems that those leading to a repltion were more likely to occur in the morning, while those that did not directly lead to a repletion were more likely to occur in the afternoon.

Why is this occurring? Perhaps, repletions in the morning are routine, but there is no routine in performing repletions in the afternoon.

repletedVsNonRepleted_PreV %>%
  ungroup() %>%
  mutate(LabHour = hour(charttime.lab)) %>%
  filter(preVsPost == "pre_repletion") %>%
  ggplot(aes(x = LabHour,fill=repleteVsNon)) + geom_histogram(position="identity",bins = 24,alpha=0.6, color = "black") + scale_x_continuous(breaks=seq(0,24,2))  + theme_pubr() + xlab("Hour of Day") + ylab("Number of Lab Values") + scale_fill_discrete(name = "", labels = c("Non-Repletion", "Repletion"))

Average pre-repletion lab value at each hour

Table
repletedVsNonRepleted_PreV %>%
  ungroup() %>%
  mutate(LabHour = hour(charttime.lab)) %>%
  filter(preVsPost == "pre_repletion") %>%
  group_by(LabHour, repleteVsNon) %>%
  summarize(mean_lab_value = mean(valuenum, na.rm = T)) %>%
  pivot_wider(names_from = repleteVsNon, values_from = mean_lab_value)
Visualization
repletedVsNonRepleted_PreV %>%
  ungroup() %>%
  mutate(LabHour = hour(charttime.lab)) %>%
  filter(preVsPost == "pre_repletion") %>%
  group_by(LabHour, repleteVsNon) %>%
  summarize(mean_lab_value = mean(valuenum, na.rm = T)) %>%
  ggbarplot(x="LabHour", y = "mean_lab_value", fill = "repleteVsNon", facet.by = "repleteVsNon" ) +
  scale_x_continuous(breaks=seq(0,24,2))  + theme_pubr()

repletedVsNonRepleted_PreV %>%
  ungroup() %>%
  mutate(LabHour = hour(charttime.lab)) %>%
  filter(preVsPost == "pre_repletion") %>%
  group_by(LabHour, repleteVsNon) %>%
  summarize(mean_lab_value = mean(valuenum, na.rm = T), std_dev = sd(valuenum, na.rm = T)) %>%
  ggplot(aes(x=LabHour,y=mean_lab_value,group=repleteVsNon, color = repleteVsNon)) + geom_line() + geom_point() + scale_x_continuous(breaks=seq(0,24,2))  + theme_pubr() + geom_errorbar(aes(ymin=mean_lab_value-std_dev, ymax=mean_lab_value+std_dev), width=.2,
                 position=position_dodge(0.05))


repletedVsNonRepleted_PreV %>%
  ungroup() %>%
  mutate(LabHour = hour(charttime.lab)) %>%
  filter(preVsPost == "pre_repletion") %>%
  group_by(LabHour, repleteVsNon) %>%
  summarize(mean_lab_value = mean(valuenum, na.rm = T)) %>%
  ggplot(aes(x=LabHour,y=mean_lab_value,fill=repleteVsNon)) + geom_bar(stat="identity", position = "dodge", color = "black") + scale_x_continuous(breaks=seq(0,24,2))  + theme_pubr()

  1. https://mimic.physionet.org/mimicdata/io/↩︎

LS0tCnRpdGxlOiAiTWltaWMgRWxlY3Ryb2x5dGUgQW5hbHlzaXMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIGRlcHRoOiAzCiAgICB0aGVtZTogc2ltcGxleAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojIyBMb2FkIExpYnJhcmllcwpMb2FkIGFsbCByZWxldmFudCBSIGxpYnJhcmllcy4KYGBge3IgTG9hZCBMaWJhcmllcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgIHJlc3VsdHM9J2hpZGUnfQojIExvYWQgTGlicmFyaWVzIApsaWJyYXJ5KCJSUG9zdGdyZXMiKQpsaWJyYXJ5KCJnZ3Bsb3QyIikKbGlicmFyeSgidGlkeXZlcnNlIikKbGlicmFyeSgiZGJwbHlyIikKbGlicmFyeSgibHVicmlkYXRlIikKbGlicmFyeSgiZGF0YS50YWJsZSIpCmxpYnJhcnkoImR0cGx5ciIpCmxpYnJhcnkoImdncHViciIpCmxpYnJhcnkoIm11U3RhdCIpCmxpYnJhcnkoIm12bm9ybXRlc3QiKQpsaWJyYXJ5KCJrbml0ciIpCmxpYnJhcnkoImthYmxlRXh0cmEiKQpgYGAKCiMjIENvbm5lY3QgdG8gUmVsYXRpb25hbCBEYXRhYmFzZSAKYGBge3IgQ29ubmVjdCB0byBSZWxhdGlvbmFsIERhdGFiYXNlLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGxvYWQgdGhlIFBvc3RncmVTUUwgZHJpdmVyCmRydiA8LSBkYkRyaXZlcigiUG9zdGdyZXMiKQoKIyBjcmVhdGUgYSBjb25uZWN0aW9uIHRvIHRoZSBwb3N0Z3JlcyBkYXRhYmFzZQojIHNldCB0aGUgc2VhcmNoIHBhdGggdG8gdGhlIG1pbWljaWlpIHNjaGVtYQpjb24gPC0gZGJDb25uZWN0KGRydiwgZGJuYW1lID0gIm1pbWljIiwKICAgICAgICAgICAgICAgICBob3N0ID0gImxvY2FsaG9zdCIsIHBvcnQgPSA1NDMyLAogICAgICAgICAgICAgICAgIHVzZXIgPSAibW91c2FnaGFubmFtIikKZGJTZW5kUXVlcnkoY29uLCAnc2V0IHNlYXJjaF9wYXRoIHRvIG1pbWljaWlpJykKYGBgCgojIyMjIFNob3cgYSBsaXN0IG9mIGFsbCB0aGUgdGFibGVzIGluIHRoZSByZWxhdGlvbmFsIGRhdGFiYXNlLiAKYGBge3J9CiMgc2hvdyBhIGxpc3Qgb2YgdGFibGVzCmRiTGlzdFRhYmxlcyhjb24pCmBgYAoKIyMjIyBGdW5jdGlvbiBmb3IgbW9yZSBlYXNpbHkgcHVsbGluZyBhIGdpdmVuIHRhYmxlLgpUaGlzIGlzIGEgbWVtb3J5IGNvbnNlcnZhdGl2ZSBhcHByb2FjaGluZyBmb3IgcGVyZm9ybWluZyBTUUwgcXVlcmllcyBvbiBEQi4gV2UgY2FuIHB1bGwgdGFibGVzIGluIG1lbW9yeSBhbmQgZG8gY2VydGFpbiBhbmFseXNlcyBvbiB0aGVtLCB3aXRob3V0IGFjdHVhbGx5IGhhdmluZyB0byBzYXZlIGEgZ2l2ZW4gb2JqZWN0IGludG8gdGhlIFIgZW52aXJvbm1lbnQuIApgYGB7cn0KI01lbW9yeSBjb25zZXJhdGl2ZSBhcHByb2FjaGluZyBmb3IgcGVyZm9ybWluZyBTUUwgcXVlcmllcyBvbiBEQiAKIyhSZXdyaXRlIHRoaXMgZnVuY3Rpb24gdG8gaGFuZGxlIG11bHRpcGxlIGlucHV0cyApCnRibF9taW1pYyA8LSBmdW5jdGlvbih0YWJsZSkgewogIHRhYmxlIDwtIGFzLmNoYXJhY3RlcihzdWJzdGl0dXRlKHRhYmxlKSkKICB0YmwoY29uLCBkYnBseXI6OmluX3NjaGVtYSgibWltaWNpaWkiLCB0YWJsZSkpCn0KYGBgCgojIyMgQ3JlYXRlIGRpY3Rpb25hcnkgb2YgZGlhZ25vc2lzIGNvZGVzClRoaXMgImRpY3Rpb25hcnkiIHdpbGwgY29udGFpbiBhbGwgb2YgdGhlIGRpYWdub3NlcyBldmVudHMgaW4gdGhlIGRhdGFiYXNlIGFzc29jaWF0ZWQgd2l0aCBhIGdpdmVuIGhvc3BpdGFsIGFkbWlzc2lvbiBpZCAoYGhhZG1faWRgKSwgYWxvbmcgd2l0aCBhIGRlc2NyaXB0aW9uIG9mIHRoZSBpY2Q5IGNvZGUuIApgYGB7cn0KI1B1bGwgZGljdGlvbmFyeSBmb3IgZGlhZ25vc2VzCmRfaWNkX2RpYWdub3NlcyA8LSB0YmxfbWltaWMoZF9pY2RfZGlhZ25vc2VzKSAlPiUKICBzZWxlY3QoaWNkOV9jb2RlLCBzaG9ydF90aXRsZSkKCmRpYWdub3Nlc19pY2QgPC10YmxfbWltaWMoZGlhZ25vc2VzX2ljZCkgJT4lCiAgc2VsZWN0KGhhZG1faWQsIGljZDlfY29kZSwgc2VxX251bSkKCiNJQ0QtOSBDb2RlcyArIHRoZWlyIGRlc2NyaXB0aW9ucwppY2Q5X2RpY3QgPC0gZGlhZ25vc2VzX2ljZCAlPiUKICBpbm5lcl9qb2luKGRfaWNkX2RpYWdub3NlcywgYnkgPSAiaWNkOV9jb2RlIikKCmljZDlfZGljdApgYGAKIyMgTG9hZCBFbGVjdHJvbHl0ZSBMYWIgTWVhc3VyZW1lbnRzCgojIyMgRGV0ZXJtaW5lIHRoZSBpZHMgY29ycmVzcG9uZGluZyB0byBsYWIgdmFsdWVzIGZvciBlYWNoIGVsZWN0cm9seXRlIAojIyMjIFBvdGFzc2l1bSAKU2F2ZWQgYWxsIG9mIHRoZSBsYWJfaWRzIGludG8gUiBlbnZpcm9ubWVudCB3aXRoIGBjb2xsZWN0KClgLCBmb3IgZmlsdGVyaW5nIGluIHRoZSBuZXh0IHN0ZXAuCmBgYHtyfQpkX2xhYml0ZW1zIDwtIHRibF9taW1pYyhkX2xhYml0ZW1zKSAjUHVsbCBkaWN0aW9uYXJ5IGZvciBsYWIgaXRlbXMgaW50byBtZW1vcnkgCgpkX2xhYml0ZW1zICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHRvbG93ZXIobGFiZWwpLCAicG90YXNzaXVtIikpICU+JQogIGNvbGxlY3QoKSAtPiBsYWJfaWRzSwoKbGFiX2lkc0sKYGBgCgojIyMjIE1hZ25lc2l1bQpgYGB7cn0KZF9sYWJpdGVtcyAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdCh0b2xvd2VyKGxhYmVsKSwgIm1hZ25lc2l1bSIpKSAlPiUKICBjb2xsZWN0KCkgLT4gbGFiX2lkc01nCgpsYWJfaWRzTWcKCmBgYAoKIyMjIyBDYWxjaXVtCgpgYGB7cn0KZF9sYWJpdGVtcyAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdCh0b2xvd2VyKGxhYmVsKSwgImNhbGNpdW0iKSkgJT4lCiAgY29sbGVjdCgpIC0+IGxhYl9pZHNDYQoKbGFiX2lkc0NhCgpgYGAKCiMjIyMgUGhvc3BoYXRlCgpgYGB7cn0KZF9sYWJpdGVtcyAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdCh0b2xvd2VyKGxhYmVsKSwgInBob3NwaGF0ZSIpKSAlPiUKICBjb2xsZWN0KCkgLT4gbGFiX2lkc1BoCgpsYWJfaWRzUGgKCmBgYAoKCiMjIyBGaWx0ZXIgdGhlbSAKV2UgYXJlIGNob29zaW5nIHRvIGluY2x1ZGUgdGhlIDFzdCBhbmQgNHRoIHJvdywgd2hpY2ggYXJlIHN0cmljdGx5IGJsb29kIGxhYiB2YWx1ZSBkcmF3cyBmb3IgcG90YXNzaXVtLiBEbyB0aGUgc2FtZSBmb3IgdGhlIG90aGVyIGVsZWN0cm9seXRlcy4gCmBgYHtyfQojRHJ1Z3MgdG8gaW5jbHVkZSBmb3IgcG90YXNzaXVtIAppdGVtaWRzSyA8LSBsYWJfaWRzSyRpdGVtaWRbYygxLDQpXQoKI0RydWdzIHRvIGluY2x1ZGUgZm9yIG1hZ25lc2l1bSAKaXRlbWlkc01nIDwtIGxhYl9pZHNNZyRpdGVtaWRbYygxKV0KCiNEcnVncyB0byBpbmNsdWRlIGZvciBDYWxjaXVtIAppdGVtaWRzQ2EgPC0gbGFiX2lkc0NhJGl0ZW1pZFtjKDQpXQoKI0RydWdzIHRvIGluY2x1ZGUgZm9yIFBob3NwaGF0ZQppdGVtaWRzUGggPC0gbGFiX2lkc1BoJGl0ZW1pZFtjKDIpXQpgYGAKCgojIyMgUHVsbCB0YWJsZSBjb250YWluaW5nIGFsbCBsYWIgbWVhc3VyZW1lbnRzLgpXZSBhcmUgbm93IHRha2luZyB0aGUgdGFibGUgYGxhYmV2ZW50c2AsIHdoaWNoIGNvbnRhaW5zIGFsbCB0aGUgbGFiIGV2ZW50cyBpbiB0aGUgZGF0YWJhc2UsIGFuZCBzZWxlY3RpbmcgdGhlIHJvd3MgYXNzb2NpYXRlZCB3aXRoIHRoZSB0d28gYmxvb2QgcG90YXNzaXVtIGxhYiB2YWx1ZXMgYGl0ZW1pZGBzIHRoYXQgd2UgY2hvc2UgYWJvdmUuIApgYGB7ciBwYWdlZC5wcmludD1UUlVFfQpsYWJldmVudHMgPC0gdGJsX21pbWljKGxhYmV2ZW50cykKCmxhYmV2ZW50cyAlPiUgCiAgZmlsdGVyKGl0ZW1pZCAlaW4lIGl0ZW1pZHNLKSAgJT4lCiAgaW5uZXJfam9pbihzZWxlY3QodGJsX21pbWljKGRfbGFiaXRlbXMpLC1sb2luY19jb2RlLCAtcm93X2lkICksIGJ5ID0gIml0ZW1pZCIpICU+JQogIHNlbGVjdChzdWJqZWN0X2lkLCBoYWRtX2lkLCBpdGVtaWQsIGNoYXJ0dGltZSwgdmFsdWVudW0sIHZhbHVldW9tLCBsYWJlbCwgZmxhZywgZmx1aWQsIGNhdGVnb3J5KSAtPiBsYWJFdmVudHNLCgpsYWJFdmVudHNLCgpsYWJldmVudHMgJT4lIAogIGZpbHRlcihpdGVtaWQgJWluJSBpdGVtaWRzTWcpICAlPiUKICBpbm5lcl9qb2luKHNlbGVjdCh0YmxfbWltaWMoZF9sYWJpdGVtcyksLWxvaW5jX2NvZGUsIC1yb3dfaWQgKSwgYnkgPSAiaXRlbWlkIikgJT4lCiAgc2VsZWN0KHN1YmplY3RfaWQsIGhhZG1faWQsIGl0ZW1pZCwgY2hhcnR0aW1lLCB2YWx1ZW51bSwgdmFsdWV1b20sIGxhYmVsLCBmbGFnLCBmbHVpZCwgY2F0ZWdvcnkpIC0+IGxhYkV2ZW50c01nCgpsYWJFdmVudHNNZwoKbGFiZXZlbnRzICU+JSAKICBmaWx0ZXIoaXRlbWlkICVpbiUgaXRlbWlkc0NhKSAgJT4lCiAgaW5uZXJfam9pbihzZWxlY3QodGJsX21pbWljKGRfbGFiaXRlbXMpLC1sb2luY19jb2RlLCAtcm93X2lkICksIGJ5ID0gIml0ZW1pZCIpICU+JQogIHNlbGVjdChzdWJqZWN0X2lkLCBoYWRtX2lkLCBpdGVtaWQsIGNoYXJ0dGltZSwgdmFsdWVudW0sIHZhbHVldW9tLCBsYWJlbCwgZmxhZywgZmx1aWQsIGNhdGVnb3J5KSAtPiBsYWJFdmVudHNDYQoKbGFiRXZlbnRzQ2EKCmxhYmV2ZW50cyAlPiUgCiAgZmlsdGVyKGl0ZW1pZCAlaW4lIGl0ZW1pZHNQaCkgICU+JQogIGlubmVyX2pvaW4oc2VsZWN0KHRibF9taW1pYyhkX2xhYml0ZW1zKSwtbG9pbmNfY29kZSwgLXJvd19pZCApLCBieSA9ICJpdGVtaWQiKSAlPiUKICBzZWxlY3Qoc3ViamVjdF9pZCwgaGFkbV9pZCwgaXRlbWlkLCBjaGFydHRpbWUsIHZhbHVlbnVtLCB2YWx1ZXVvbSwgbGFiZWwsIGZsYWcsIGZsdWlkLCBjYXRlZ29yeSkgLT4gbGFiRXZlbnRzUGgKCmxhYkV2ZW50c1BoCgoKIyBwcmludChwYXN0ZSgiIFRoZXJlIGFyZSAiLHB1bGwobnVtTGFiS2V2ZW50c1sxLDFdKSwgIiB0b3RhbCBwb3Rhc3NpdW0gbGFiIHZhbHVlcyB0aGF0IHdlcmUgZXh0cmFjdGVkIGZyb20gdGhlIGxhYmV2ZW50cyB0YWJsZS4iICkpCgpgYGAKCgoKIyMgRGVmaW5lIHJlcGxldGlvbiBldmVudHMgd2l0aGluIHRoZSBtZXRhdmlldyBkYXRhYmFzZQojIyMgUG90YXNzaXVtClRoZSBgZF9pdGVtc2AgdGFibGUgaW4gbWltaWNzIGNvbnRhaW5zIGEgZGljdGlvbmFyeSBmb3IgYWxsIG9mIHRoZSBldmVudHMgYXNzb2NpYXRlZCB3aXRoIGJvdGggdGhlIG1ldGF2aWV3IGFuZCBjYXJldmlldyBkYXRhYmFzZS4gV2UgYXJlIG9ubHkgZ29pbmcgdG8gZm9jdXMgb24gdGhlIG1ldGF2aWV3IGRhdGFiYXNlIGF0IHRoaXMgcG9pbnQuIFdlIHRoZW4gd2FudCB0byBmaWx0ZXIgdGhpcyB0YWJsZSBieSB0aGUgbGFiZWwgZGVzY3JpcHRpb24gZm9yIGl0ZW1zIHRoYXQgYXJlIHJlbGF0ZWQgdG8gcG90YXNzaXVtLiAKYGBge3J9CmRfaXRlbXMgPC0gdGJsX21pbWljKGRfaXRlbXMpICNQdWxsIGRpY3Rpb25hcnkgZm9yIGFsbCBsYWIgZXZlbnRzIGludG8gbWVtb3J5IAoKZF9pdGVtcyAlPiUKICBmaWx0ZXIobGFiZWwgJWlsaWtlJSAiJXBvdGFzc2l1bSUiIHwgbGFiZWwgJWlsaWtlJSAiJWtjbCUiKSAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdCh0b2xvd2VyKGRic291cmNlKSwgIm1ldGEiKSkgJT4lCiAgYXJyYW5nZShsYWJlbCkgLT4ga19pdGVtc19NVgoKa19pdGVtc19NViAKCmBgYApBbHRob3VnaCB0aGVyZSBhcmUgZXZlbnRzIHNvdXJjZWQgZnJvbSBlaXRoZXIgbWV0YXZpZXcgb3IgY2FyZXZpZXcsIHRoZXNlIHR3byBob3NwaXRhbCBkYXRhYmFzZXMgYXJlIGFjdHVhbGx5IGxpbmtlZCB0byAzIGRpZmZlcmVudCB0YWJsZXMgaW4gdGhlIG1pbWljcyBkYXRhYmFzZTogYGlucHV0ZXZlbnRzX212YCwgYGlucHV0ZXZlbnRzX2N2YCBvciBgY2hhcnRldmVudHMuYCBTaW5jZSB3ZSBhcmUgZXhhbWluaW5nIG9ubHkgdGhlIG1ldGF2aXNpb24gZGF0YWJhc2UsIGl0IHdpbGwgb25seSBsaW5rIHRvIGBpbnB1dGV2ZW50c19tdmAgYW5kIGBjaGFydGV2ZW50cy5gIFdlIHdvdWxkIGxpa2UgdG8gc2VsZWN0IGZvciBvbmx5IHRoZSBldmVudHMgdGhhdCBsaW5rIHRvICBgaW5wdXRldmVudHNfbXZgLgoKYGBge3J9CmtfaXRlbXNfTVYgJT4lIAogIGZpbHRlcihsaW5rc3RvPT0iaW5wdXRldmVudHNfbXYiKSAlPiUgY29sbGVjdCgpIC0+IGtfaXRlbXNfTVZfSU0KCmtfaXRlbXNfTVZfSU0KYGBgCklmIHlvdSBsb29rIGF0IHRoZSBhYm92ZSB0YWJsZSwgdGhlIGZpcnN0IHJvdyBoYXMgYSBLQ0wgKEJvbHVzKSB3aXRoIHVuaXQgbUwsIHdoaWxlIHRoZSA1dGggcm93IGhhcyBQb3Rhc3NpdW0gQ2hsb3JpZGUgd2l0aCB1bml0cyBpbiBtRXEuIEFjY29yZGluZyB0byB0aGUgbWltaWNzIGlpaSB3ZWJzaXRlOiAKCj4gIk1ldGF2aXNpb24gcmVjb3JkcyBJTyBkYXRhIHVzaW5nIHR3byB0YWJsZXM6IFJBTkdFU0lHTkFMUyBhbmQgT1JERVJFTlRSWS4gVGhlc2UgdGFibGVzIGRvIG5vdCBhcHBlYXIgaW4gTUlNSUMtSUlJIGFzIHRoZXkgaGF2ZSBiZWVuIG1lcmdlZCB0byBmb3JtIHRoZSA+IElOUFVURVZFTlRTX01WIHRhYmxlLiBSQU5HRVNJR05BTFMgY29udGFpbnMgcmVjb3JkZWQgZGF0YSBlbGVtZW50cyB3aGljaCBsYXN0IGZvciBhIGZpeGVkIHBlcmlvZCBvZiB0aW1lLiBGdXJ0aGVybW9yZSwgdGhlIFJBTkdFU0lHTkFMUyB0YWJsZSByZWNvcmRlZCA+IGluZm9ybWF0aW9uIGZvciBlYWNoIGNvbXBvbmVudCBvZiB0aGUgZHJ1ZyBzZXBhcmF0ZWx5LiBGb3IgZXhhbXBsZSwgZm9yIGEgbm9yZXBpbmVwaHJpbmUgYWRtaW5pc3RyYXRpb24gdGhlcmUgd291bGQgYmUgdHdvIGNvbXBvbmVudHM6IGEgbWFpbiBvcmRlciAgICA+IGNvbXBvbmVudCAobm9yZXBpbmVwaHJpbmUpIGFuZCBhIHNvbHV0aW9uIGNvbXBvbmVudCAoTmFDbCkuIiBbXjFdCgpbXjFdOiA8ZW0+aHR0cHM6Ly9taW1pYy5waHlzaW9uZXQub3JnL21pbWljZGF0YS9pby88L2VtPiAKClRoZXJlZm9yZSwgd2Ugd2lsbCBvbmx5IGJlIHNlbGVjdGluZyByZXBsZXRpb25zIGJhc2VkIG9mZiBvZiB0aGUgIm1haW4gb3JkZXIgY29tcG9uZW50IiwgYW5kIGlnbm9yaW5nIHRoZSBzb2x1dGlvbi4gV2Ugd2lsbCBzZWxlY3QgZm9yIHRoZSA0dGggYW5kIDV0aCByb3dzLCB3aGljaCBjb3JyZXNwb25kIHRvIFBvdGFzc2l1bWUgQWNldGF0ZSBhbmQgUG90YXNzaXVtIENobG9yaWRlLiAKCmBgYHtyfQprX2l0ZW1zX01WX0lNICU+JSBzbGljZSgtNiwtMywgLTIsLTEpIC0+IGtfaXRlbXNfTVZfSU0gI09OTFkgS0VFUElORyBUSEUgUkVQTEVUSU9OUywgVEhFIEFERElUSVZFIEFNT1VOVFMsIE5PVCBUSEUgQk9MVVMgCmtfaXRlbXNfTVZfSU0KCmtfaXRlbXNfTVZfSU0gJT4lIHB1bGwoaXRlbWlkKSAtPiBrX2l0ZW1zX01WX0lNX3ZlY3RvciAKYGBgCgojIyMgTWFnbmVzaXVtCmBgYHtyfQpkX2l0ZW1zIDwtIHRibF9taW1pYyhkX2l0ZW1zKSAjUHVsbCBkaWN0aW9uYXJ5IGZvciBhbGwgbGFiIGV2ZW50cyBpbnRvIG1lbW9yeSAKCmRfaXRlbXMgJT4lCiAgZmlsdGVyKGxhYmVsICVpbGlrZSUgIiVtYWduZXNpdW0lIiB8IGxhYmVsICVpbGlrZSUgIiVtZyUiKSAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdCh0b2xvd2VyKGRic291cmNlKSwgIm1ldGEiKSkgJT4lCiAgYXJyYW5nZShsYWJlbCkgLT4gbWdfaXRlbXNfTVYKCm1nX2l0ZW1zX01WICU+JSAKICBmaWx0ZXIobGlua3N0bz09ImlucHV0ZXZlbnRzX212IikgJT4lIGNvbGxlY3QoKSAtPiBtZ19pdGVtc19NVl9JTQoKbWdfaXRlbXNfTVZfSU0KCm1nX2l0ZW1zX01WX0lNICU+JSBzbGljZSgxKSAtPiBtZ19pdGVtc19NVl9JTSAjT05MWSBLRUVQSU5HIFRIRSBSRVBMRVRJT05TLCBUSEUgQURESVRJVkUgQU1PVU5UUywgTk9UIFRIRSBCT0xVUyAKbWdfaXRlbXNfTVZfSU0KCm1nX2l0ZW1zX01WX0lNICU+JSBwdWxsKGl0ZW1pZCkgLT4gbWdfaXRlbXNfTVZfSU1fdmVjdG9yIAoKYGBgCgojIyMgQ2FsY2l1bQpgYGB7cn0KZF9pdGVtcyAlPiUKICBmaWx0ZXIobGFiZWwgJWlsaWtlJSAiJWNhbGNpdW0lIiApICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHRvbG93ZXIoZGJzb3VyY2UpLCAibWV0YSIpKSAlPiUKICBhcnJhbmdlKGxhYmVsKSAtPiBjYV9pdGVtc19NVgoKY2FfaXRlbXNfTVYgCgpjYV9pdGVtc19NViAlPiUgCiAgZmlsdGVyKGxpbmtzdG89PSJpbnB1dGV2ZW50c19tdiIpICU+JSBjb2xsZWN0KCkgLT4gY2FfaXRlbXNfTVZfSU0KCmNhX2l0ZW1zX01WX0lNCgpjYV9pdGVtc19NVl9JTSAlPiUgc2xpY2UoMSkgLT4gY2FfaXRlbXNfTVZfSU0gI09OTFkgS0VFUElORyBUSEUgUkVQTEVUSU9OUywgVEhFIEFERElUSVZFIEFNT1VOVFMsIE5PVCBUSEUgQk9MVVMgCmNhX2l0ZW1zX01WX0lNCgpjYV9pdGVtc19NVl9JTSAlPiUgcHVsbChpdGVtaWQpIC0+IGNhX2l0ZW1zX01WX0lNX3ZlY3RvciAKCmBgYAoKIyMjIFBob3NwaGF0ZQpgYGB7cn0KZF9pdGVtcyAlPiUKICBmaWx0ZXIobGFiZWwgJWlsaWtlJSAiJXBob3NwaGF0ZSUiICB8IGxhYmVsICVpbGlrZSUgIm5hJSIpICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHRvbG93ZXIoZGJzb3VyY2UpLCAibWV0YSIpKSAlPiUKICBhcnJhbmdlKGxhYmVsKSAtPiBwaF9pdGVtc19NVgoKcGhfaXRlbXNfTVYgCgpwaF9pdGVtc19NViAlPiUgCiAgZmlsdGVyKGxpbmtzdG89PSJpbnB1dGV2ZW50c19tdiIpICU+JSBjb2xsZWN0KCkgLT4gcGhfaXRlbXNfTVZfSU0KCnBoX2l0ZW1zX01WX0lNCgpwaF9pdGVtc19NVl9JTSAlPiUgc2xpY2UoMSw4KSAtPiBwaF9pdGVtc19NVl9JTSAjT05MWSBLRUVQSU5HIFRIRSBSRVBMRVRJT05TLCBUSEUgQURESVRJVkUgQU1PVU5UUywgTk9UIFRIRSBCT0xVUyAKcGhfaXRlbXNfTVZfSU0KCnBoX2l0ZW1zX01WX0lNICU+JSBwdWxsKGl0ZW1pZCkgLT4gcGhfaXRlbXNfTVZfSU1fdmVjdG9yIApgYGAKCiMjIFB1bGwgcmVwbGV0aW9uIGV2ZW50cyBmcm9tIG1ldGF2aWV3IGRhdGFic2UKIyMjIFBvdGFzc2l1bSAKV2Ugd2lsbCBub3cgdXNlIHRoZSBmaWx0ZXJlZCBpZHMgZm9yIHRoZSB0d28gYWRkaXRpdmUgc29sdXRpb25zIGFib3ZlIHRvIHB1bGwgZWxlY3Ryb2x5dGUgcmVwbGV0aW9ucyBmcm9tIHRoZSBgaW5wdXRldmVudHNfbXZgIHRhYmxlLgpgYGB7cn0KaW5wdXRldmVudHNfbXYgPC0gdGJsX21pbWljKGlucHV0ZXZlbnRzX212KQoKaW5wdXRldmVudHNfbXYgJT4lCiAgZmlsdGVyKGl0ZW1pZCAlaW4lIGtfaXRlbXNfTVZfSU1fdmVjdG9yKSAlPiUKICBzZWxlY3Qoc3ViamVjdF9pZCwgaGFkbV9pZCxpY3VzdGF5X2lkLCBsaW5rb3JkZXJpZCwgb3JkZXJpZCwgaXRlbWlkLCBzdGFydHRpbWUsIGVuZHRpbWUsIGFtb3VudCwgYW1vdW50dW9tLCByYXRlLCByYXRldW9tLCBzdGF0dXNkZXNjcmlwdGlvbiwgb3JkZXJjb21wb25lbnR0eXBlZGVzY3JpcHRpb24pICU+JQogIGZpbHRlcighc3RhdHVzZGVzY3JpcHRpb24gPT0gIlJld3JpdHRlbiIpICU+JQogIHJlbmFtZShpdGVtaWQucmVwbGV0aW9uID0gaXRlbWlkKSAtPiByZXBFdmVudHNLCgpyZXBFdmVudHNLCmBgYApEZXRlcm1pbmUgdGhlIGFtb3VudCBvZiBwb3Rhc3NpdW0gcmVwbGV0aW9uczogCmBgYHtyIFBvdGFzc2l1bSBSZXBsZXRpb24gQ291bnR9CnJlcEV2ZW50c0sgJT4lIGNvdW50KCkgJT4lIGNvbGxlY3QoKSAtPiBudW1LUmVwbGV0aW9ucwoKcHJpbnQocGFzdGUoIiBUaGVyZSBhcmUgIixwdWxsKG51bUtSZXBsZXRpb25zWzEsMV0pLCAiIHRvdGFsIHBvdGFzc2l1bSByZXBsZXRpb25zIHRoYXQgd2VyZSBleHRyYWN0ZWQgZnJvbSB0aGUgaW5wdXRldmVudHNfbXYgdGFibGUuIiApKQoKYGBgCiMjIyBNYWduZXNpdW0KYGBge3J9CmlucHV0ZXZlbnRzX212ICU+JQogIGZpbHRlcihpdGVtaWQgJWluJSBtZ19pdGVtc19NVl9JTV92ZWN0b3IpICU+JQogIHNlbGVjdChzdWJqZWN0X2lkLCBoYWRtX2lkLGljdXN0YXlfaWQsIGxpbmtvcmRlcmlkLCBvcmRlcmlkLCBpdGVtaWQsIHN0YXJ0dGltZSwgZW5kdGltZSwgYW1vdW50LCBhbW91bnR1b20sIHJhdGUsIHJhdGV1b20sIHN0YXR1c2Rlc2NyaXB0aW9uLCBvcmRlcmNvbXBvbmVudHR5cGVkZXNjcmlwdGlvbikgJT4lCiAgZmlsdGVyKCFzdGF0dXNkZXNjcmlwdGlvbiA9PSAiUmV3cml0dGVuIikgJT4lCiAgcmVuYW1lKGl0ZW1pZC5yZXBsZXRpb24gPSBpdGVtaWQpIC0+IHJlcEV2ZW50c01nCgpyZXBFdmVudHNNZwpgYGAKCiMjIyBDYWxjaXVtCmBgYHtyfQppbnB1dGV2ZW50c19tdiAlPiUKICBmaWx0ZXIoaXRlbWlkICVpbiUgY2FfaXRlbXNfTVZfSU1fdmVjdG9yKSAlPiUKICBzZWxlY3Qoc3ViamVjdF9pZCwgaGFkbV9pZCxpY3VzdGF5X2lkLCBsaW5rb3JkZXJpZCwgb3JkZXJpZCwgaXRlbWlkLCBzdGFydHRpbWUsIGVuZHRpbWUsIGFtb3VudCwgYW1vdW50dW9tLCByYXRlLCByYXRldW9tLCBzdGF0dXNkZXNjcmlwdGlvbiwgb3JkZXJjb21wb25lbnR0eXBlZGVzY3JpcHRpb24pICU+JQogIGZpbHRlcighc3RhdHVzZGVzY3JpcHRpb24gPT0gIlJld3JpdHRlbiIpICU+JQogIHJlbmFtZShpdGVtaWQucmVwbGV0aW9uID0gaXRlbWlkKSAtPiByZXBFdmVudHNDYQoKcmVwRXZlbnRzQ2EKYGBgCgojIyMgUGhvc3BoYXRlCmBgYHtyfQppbnB1dGV2ZW50c19tdiAlPiUKICBmaWx0ZXIoaXRlbWlkICVpbiUgcGhfaXRlbXNfTVZfSU1fdmVjdG9yKSAlPiUKICBzZWxlY3Qoc3ViamVjdF9pZCwgaGFkbV9pZCxpY3VzdGF5X2lkLCBsaW5rb3JkZXJpZCwgb3JkZXJpZCwgaXRlbWlkLCBzdGFydHRpbWUsIGVuZHRpbWUsIGFtb3VudCwgYW1vdW50dW9tLCByYXRlLCByYXRldW9tLCBzdGF0dXNkZXNjcmlwdGlvbiwgb3JkZXJjb21wb25lbnR0eXBlZGVzY3JpcHRpb24pICU+JQogIGZpbHRlcighc3RhdHVzZGVzY3JpcHRpb24gPT0gIlJld3JpdHRlbiIpICU+JQogIHJlbmFtZShpdGVtaWQucmVwbGV0aW9uID0gaXRlbWlkKSAtPiByZXBFdmVudHNQaAoKcmVwRXZlbnRzUGgKYGBgCgoKIyMgSm9pbiB0YWJsZXMgY29udGFpbmluZyByZXBsZXRpb24gZXZlbnRzIGFuZCBsYWIgZXZlbnRzIApCb3RoIHRoZSB0YWJsZXMgY29udGFpbmluZyB0aGUgbGFiIGRyYXdzIGFuZCB0aGUgcmVwbGV0aW9uIGV2ZW50cyBoYXZlIGJlZW4gZXh0cmFjdGVkLiBXZSBjYW4gbm93IGpvaW4gdGhlbSB0byBiZWdpbiBhbmFseXppbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZW0uIAoKSW4gYWRkaXRpb24gdG8gam9pbmluZyB0aGUgdGFibGVzLCB3ZSBoYXZlIHJlbmFtZWQgdGhlIGNvbHVtbiBjb3JyZXNwb25kaW5nIHRvIHRoZSBtb21lbnQgdGhlIGZsdWlkIGNvbGxlY3Rpb24gZm9yIHRoZSBsYWIgdmFsdWUgd2FzIHJlY29yZGVkLCBgY2hhcnR0aW1lYCwgYXMgYGNoYXJ0dGltZS5sYWJgIGZvciBjbGFyaXR5LiAKYGBge3IgSm9pbiBSZXBsZXRpb25zIGFuZCBMYWIgRXZlbnRzfQpyZXBFdmVudHNLICU+JSAKICBpbm5lcl9qb2luKGxhYkV2ZW50c0ssIGJ5PWMoInN1YmplY3RfaWQiID0gInN1YmplY3RfaWQiLCAiaGFkbV9pZCIgPSAiaGFkbV9pZCIpKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIHJlbmFtZShjaGFydHRpbWUubGFiID0gY2hhcnR0aW1lKSAlPiUKICBjb2xsZWN0KCkgLT4ga19sYWJfcmVwbGV0aW9uc19NVl9uZXcKCmtfbGFiX3JlcGxldGlvbnNfTVZfbmV3CgojTWFnbmVzaXVtCnJlcEV2ZW50c01nICU+JSAKICBpbm5lcl9qb2luKGxhYkV2ZW50c01nLCBieT1jKCJzdWJqZWN0X2lkIiA9ICJzdWJqZWN0X2lkIiwgImhhZG1faWQiID0gImhhZG1faWQiKSkgJT4lCiAgZGlzdGluY3QoKSAlPiUKICByZW5hbWUoY2hhcnR0aW1lLmxhYiA9IGNoYXJ0dGltZSkgJT4lCiAgY29sbGVjdCgpIC0+IG1nX2xhYl9yZXBsZXRpb25zX01WX25ldwoKbWdfbGFiX3JlcGxldGlvbnNfTVZfbmV3CgojQ2FsY2l1bSAKcmVwRXZlbnRzQ2EgJT4lIAogIGlubmVyX2pvaW4obGFiRXZlbnRzQ2EsIGJ5PWMoInN1YmplY3RfaWQiID0gInN1YmplY3RfaWQiLCAiaGFkbV9pZCIgPSAiaGFkbV9pZCIpKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIHJlbmFtZShjaGFydHRpbWUubGFiID0gY2hhcnR0aW1lKSAlPiUKICBjb2xsZWN0KCkgLT4gY2FfbGFiX3JlcGxldGlvbnNfTVZfbmV3CgpjYV9sYWJfcmVwbGV0aW9uc19NVl9uZXcKI1Bob3NwaGF0ZQpyZXBFdmVudHNQaCAlPiUgCiAgaW5uZXJfam9pbihsYWJFdmVudHNQaCwgYnk9Yygic3ViamVjdF9pZCIgPSAic3ViamVjdF9pZCIsICJoYWRtX2lkIiA9ICJoYWRtX2lkIikpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgcmVuYW1lKGNoYXJ0dGltZS5sYWIgPSBjaGFydHRpbWUpICU+JQogIGNvbGxlY3QoKSAtPiBwaF9sYWJfcmVwbGV0aW9uc19NVl9uZXcKCnBoX2xhYl9yZXBsZXRpb25zX01WX25ldwoKI0NvbWJpbmUgaW50byBvbmUgZGF0YXNldAphbGxfcmVwbGV0aW9uc19NVl9uZXdfQUVzIDwtIGJpbmRfcm93cyhsaXN0KCBwb3Rhc3NpdW0gPSBrX2xhYl9yZXBsZXRpb25zX01WX25ldywgbWFnbmVzaXVtID0gbWdfbGFiX3JlcGxldGlvbnNfTVZfbmV3LCBjYWxjaXVtID0gY2FfbGFiX3JlcGxldGlvbnNfTVZfbmV3LCBwaG9zcGhhdGUgPSBwaF9sYWJfcmVwbGV0aW9uc19NVl9uZXcpLCAuaWQgPSAiRWxlY3Ryb2x5dGUiKQoKYWxsX3JlcGxldGlvbnNfTVZfbmV3X0FFcwpgYGAKIyMjIEZpbmQgcmVjZW50IHJlcGxldGlvbnMKV2Ugd2FudCB0byBmbGFnIGFsbCByZXBsZXRpb24gZXZlbnRzIHRoYXQgb2NjdXJyZWQgZWl0aGVyIDI0IGhvdXJzIGJlZm9yZSBvciAyNCBob3VycyBhZnRlciBhIGdpdmVuIGxhYiB2YWx1ZS4gV2UgdGhlbiBmaWx0ZXIgdGhlIHRhYmxlLCBzdG9yaW5nIGFsbCBvZiB0aGUgcmVwbGV0aW9ucyBvY2N1cnJpbmcgQkVGT1JFIGEgZ2l2ZW4gbGFiIHZhbHVlIGluIG9uZSB0YWJsZSwgYW5kIGFsbCBvZiB0aGUgb25lcyBvY2N1cnJpbmcgQUZURVIgYSBnaXZlbiBsYWIgdmFsdWUgaW4gYW5vdGhlci4gCmBgYHtyfQojICNGaW5kIHRoZSBSRUNFTlQgUmVwbGV0aW9ucwojIGtfbGFiX3JlcGxldGlvbnNfTVZfbmV3ICU+JQojICAgbXV0YXRlKGNoYXJ0dGltZS5sYWI9IGFzX2RhdGV0aW1lKGNoYXJ0dGltZS5sYWIpLCBlbmR0aW1lPWFzX2RhdGV0aW1lKGVuZHRpbWUpLCBzdGFydHRpbWUgPSBhc19kYXRldGltZShzdGFydHRpbWUpKSAlPiUKIyAgIG11dGF0ZShpc1JlY2VudFByZSA9IGRpZmZ0aW1lKHN0YXJ0dGltZSwgY2hhcnR0aW1lLmxhYiwgdW5pdHMgPSAiaG91cnMiKSA8PSAyNCAmIGRpZmZ0aW1lKHN0YXJ0dGltZSwgY2hhcnR0aW1lLmxhYiwgdW5pdHMgPSAiaG91cnMiKSA+IDAgKSAlPiUKIyAgIG11dGF0ZShpc1JlY2VudFBvc3QgPSBkaWZmdGltZShlbmR0aW1lLCBjaGFydHRpbWUubGFiLCB1bml0cyA9ICJob3VycyIpID49IC0yNCAmIGRpZmZ0aW1lKGVuZHRpbWUsIGNoYXJ0dGltZS5sYWIsIHVuaXRzID0gImhvdXJzIikgPCAwICkgIC0+IGFsbFJlcExhYkV2ZW50c19rCiMgCiMgbWdfbGFiX3JlcGxldGlvbnNfTVZfbmV3ICU+JQojICAgbXV0YXRlKGNoYXJ0dGltZS5sYWI9IGFzX2RhdGV0aW1lKGNoYXJ0dGltZS5sYWIpLCBlbmR0aW1lPWFzX2RhdGV0aW1lKGVuZHRpbWUpLCBzdGFydHRpbWUgPSBhc19kYXRldGltZShzdGFydHRpbWUpKSAlPiUKIyAgIG11dGF0ZShpc1JlY2VudFByZSA9IGRpZmZ0aW1lKHN0YXJ0dGltZSwgY2hhcnR0aW1lLmxhYiwgdW5pdHMgPSAiaG91cnMiKSA8PSAyNCAmIGRpZmZ0aW1lKHN0YXJ0dGltZSwgY2hhcnR0aW1lLmxhYiwgdW5pdHMgPSAiaG91cnMiKSA+IDAgKSAlPiUKIyAgIG11dGF0ZShpc1JlY2VudFBvc3QgPSBkaWZmdGltZShlbmR0aW1lLCBjaGFydHRpbWUubGFiLCB1bml0cyA9ICJob3VycyIpID49IC0yNCAmIGRpZmZ0aW1lKGVuZHRpbWUsIGNoYXJ0dGltZS5sYWIsIHVuaXRzID0gImhvdXJzIikgPCAwICkgIC0+IGFsbFJlcExhYkV2ZW50c19tZwojIAojIGNhX2xhYl9yZXBsZXRpb25zX01WX25ldyAlPiUKIyAgIG11dGF0ZShjaGFydHRpbWUubGFiPSBhc19kYXRldGltZShjaGFydHRpbWUubGFiKSwgZW5kdGltZT1hc19kYXRldGltZShlbmR0aW1lKSwgc3RhcnR0aW1lID0gYXNfZGF0ZXRpbWUoc3RhcnR0aW1lKSkgJT4lCiMgICBtdXRhdGUoaXNSZWNlbnRQcmUgPSBkaWZmdGltZShzdGFydHRpbWUsIGNoYXJ0dGltZS5sYWIsIHVuaXRzID0gImhvdXJzIikgPD0gMjQgJiBkaWZmdGltZShzdGFydHRpbWUsIGNoYXJ0dGltZS5sYWIsIHVuaXRzID0gImhvdXJzIikgPiAwICkgJT4lCiMgICBtdXRhdGUoaXNSZWNlbnRQb3N0ID0gZGlmZnRpbWUoZW5kdGltZSwgY2hhcnR0aW1lLmxhYiwgdW5pdHMgPSAiaG91cnMiKSA+PSAtMjQgJiBkaWZmdGltZShlbmR0aW1lLCBjaGFydHRpbWUubGFiLCB1bml0cyA9ICJob3VycyIpIDwgMCApICAtPiBhbGxSZXBMYWJFdmVudHNfY2EKIyAKIyBwaF9sYWJfcmVwbGV0aW9uc19NVl9uZXcgJT4lCiMgICBtdXRhdGUoY2hhcnR0aW1lLmxhYj0gYXNfZGF0ZXRpbWUoY2hhcnR0aW1lLmxhYiksIGVuZHRpbWU9YXNfZGF0ZXRpbWUoZW5kdGltZSksIHN0YXJ0dGltZSA9IGFzX2RhdGV0aW1lKHN0YXJ0dGltZSkpICU+JQojICAgbXV0YXRlKGlzUmVjZW50UHJlID0gZGlmZnRpbWUoc3RhcnR0aW1lLCBjaGFydHRpbWUubGFiLCB1bml0cyA9ICJob3VycyIpIDw9IDI0ICYgZGlmZnRpbWUoc3RhcnR0aW1lLCBjaGFydHRpbWUubGFiLCB1bml0cyA9ICJob3VycyIpID4gMCApICU+JQojICAgbXV0YXRlKGlzUmVjZW50UG9zdCA9IGRpZmZ0aW1lKGVuZHRpbWUsIGNoYXJ0dGltZS5sYWIsIHVuaXRzID0gImhvdXJzIikgPj0gLTI0ICYgZGlmZnRpbWUoZW5kdGltZSwgY2hhcnR0aW1lLmxhYiwgdW5pdHMgPSAiaG91cnMiKSA8IDAgKSAgLT4gYWxsUmVwTGFiRXZlbnRzX3BoCgphbGxfcmVwbGV0aW9uc19NVl9uZXdfQUVzICU+JQogIG11dGF0ZShjaGFydHRpbWUubGFiPSBhc19kYXRldGltZShjaGFydHRpbWUubGFiKSwgZW5kdGltZT1hc19kYXRldGltZShlbmR0aW1lKSwgc3RhcnR0aW1lID0gYXNfZGF0ZXRpbWUoc3RhcnR0aW1lKSkgJT4lCiAgbXV0YXRlKGlzUmVjZW50UHJlID0gZGlmZnRpbWUoc3RhcnR0aW1lLCBjaGFydHRpbWUubGFiLCB1bml0cyA9ICJob3VycyIpIDw9IDI0ICYgZGlmZnRpbWUoc3RhcnR0aW1lLCBjaGFydHRpbWUubGFiLCB1bml0cyA9ICJob3VycyIpID4gMCApICU+JQogIG11dGF0ZShpc1JlY2VudFBvc3QgPSBkaWZmdGltZShlbmR0aW1lLCBjaGFydHRpbWUubGFiLCB1bml0cyA9ICJob3VycyIpID49IC0yNCAmIGRpZmZ0aW1lKGVuZHRpbWUsIGNoYXJ0dGltZS5sYWIsIHVuaXRzID0gImhvdXJzIikgPCAwICkgLT4gYWxsX2VsZWN0cm9seXRlX3JlcExhYkV2ZW50cwoKI0ZpbHRlciBvbmVzIHRoYXQgb2NjdXJyZWQgaW4gcGFzdCAyNCBob3VycyAKYWxsX2VsZWN0cm9seXRlX3JlcExhYkV2ZW50cyAlPiUKICBmaWx0ZXIoaXNSZWNlbnRQb3N0IHwgaXNSZWNlbnRQcmUpIAoKI0NvbWJpbmUgdGhlbSBhbGwgaW50byBvbmUgZGF0YXNldCAKYWxsX2VsZWN0cm9seXRlX3JlcExhYkV2ZW50cyA8LSBiaW5kX3Jvd3MobGlzdCggcG90YXNzaXVtID0gYWxsUmVwTGFiRXZlbnRzX2ssIG1hZ25lc2l1bSA9IGFsbFJlcExhYkV2ZW50c19tZywgY2FsY2l1bSA9IGFsbFJlcExhYkV2ZW50c19jYSwgcGhvc3BoYXRlID0gYWxsUmVwTGFiRXZlbnRzX3BoKSwgLmlkID0gIkVsZWN0cm9seXRlIikKCmFsbF9lbGVjdHJvbHl0ZV9yZXBMYWJFdmVudHMgJT4lCiAgZmlsdGVyKGlzUmVjZW50UG9zdCB8IGlzUmVjZW50UHJlKQpgYGAKCgojIyBBbmFseXNpcyBhbmQgVmlzdWFsaXphdGlvbiAKV2UgYXJlIGdvaW5nIHRvIGRvIGFuYWx5c2lzIGhlcmUgb24gdGhlIGRhdGFzZXQgY29udGFpbmluZyByZXBsZXRpb25zLiBXZSBhcmUgbWFraW5nIHRoaXMgZGlzdGluY3Rpb24sIGFzIGxhdGVyIG9uLCB3ZSB3aWxsIGRvIGFuYWx5c2lzIG9uIG5vbi1yZXBsZXRpb25zLiAKCiMjIyBBbmFseXNpcyBvbiBSZXBsZXRpb25zCiMjIyMgVGFibGUKSW4gdGhpcyB0YWJsZSB3ZSBhcmUganVzdCBkb2luZyBzb21lIHN1bW1hcnkgYW5hbHlzaXMgb24gdGhlIGRhdGFzZXQuIAoKYGBge3J9CnBvc3RFeGNsdXNpb25zS19SZXBsZXRlZCAlPiUKICBncm91cF9ieSggcHJlVnNQb3N0KSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSwgbWVhbiA9IG1lYW4odmFsdWVudW0sIG5hLnJtID0gVFJVRSksIHN0YW5kYXJkX2RldmlhdGlvbiA9IHNkKHZhbHVlbnVtLCBuYS5ybSA9IFQpKSAKYGBgCgoKIyMjIEV4dHJhY3QgdGhlIG1vc3QgcmVjZW50IHJlcGxldGlvbiwgcHJpb3IgYW5kIGFmdGVyIGEgZ2l2ZW4gbGFiIHZhbHVlLiAKV2UgaGF2ZSBhIHRhYmxlIHRoYXQgY29udGFpbnMgYWxsIG9mIHRoZSByZXBsZXRpb24gZXZlbnRzIHRoYXQgb2NjdXIgZWl0aGVyIDI0IGhvdXJzIHByaW9yIG9yIGFmdGVyIGEgZ2l2ZW4gbGFiIHZhbHVlLiBXZSB0aGVuIGdyb3VwIHRoZSByb3dzIGJ5IGVhY2ggc3ViamVjdCBhbmQgaG9zcGl0YWwgaWQsIGFzIHdlbGwgYXMgdGhlIHRpbWUgb2YgZmx1aWQgYWNxdWlzaXRpb24gZm9yIHRoZSBsYWIgdmFsdWUuIFRoZW4sIHdlIGNyZWF0ZSBhIGNvbHVtbiB0aGF0IGdlbmVyYXRlcyBhIGZsYWcgaWYgdGhlIGxhYiB2YWx1ZSB0aGF0IGlzIGNob3NlbiBpcyB0aGUgbWluaW11bSBgc3RhcnR0aW1lYCBmb3IgYWxsIHRoZSBwb3NzaWJsZSBgc3RhcnR0aW1lYHMuIEJlY2F1c2UgdGhlcmUgYXJlIG11bHRpcGxlIGxhYiB2YWx1ZXMsIGFzIHdlbGwgYXMgbXVsdGlwbGUgcmVwbGV0aW9uIGV2ZW50cywgZm9yIGEgZ2l2ZW4gYGhhZG1faWRgIHRoZSB0YWJsZSBvdXRwdXR0ZWQgaGFzIGFsbCB0aGUgcG9zc2libGUgcGVybXV0YXRpb25zLiBXZSB3YW50IHRvIHNlbGVjdCB0aGUgbW9zdCByZWNlbnQgcmVwbGV0aW9uIEFGVEVSIGEgbGFiIHZhbHVlLCBzbyB3ZSBjaG9vc2UgdGhlIHJlcGxldGlvbiB3aXRoIHRoZSBgc3RhcnR0aW1lYCBlcXVhbCB0byB0aGUgbWluaW11bSBgc3RhcnR0aW1lYCBmb3IgYSBnaXZlbiBsYWIgdmFsdWUuIAoKYGBge3J9CiNQb3Rhc3NpdW0gbGFiIGV2ZW50IFBSRS1yZXBsZXRpb25zIAphbGxSZXBMYWJFdmVudHNfayAlPiUKICBmaWx0ZXIoaXNSZWNlbnRQcmUpICU+JSAKICBncm91cF9ieShzdWJqZWN0X2lkLCBoYWRtX2lkLGNoYXJ0dGltZS5sYWIpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRSZXBsZXRpb24gPSBzdGFydHRpbWUgPT0gbWluKHN0YXJ0dGltZSkpICU+JQogIGZpbHRlcihpc01vc3RSZWNlbnRSZXBsZXRpb24pICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgZ3JvdXBfYnkoc3ViamVjdF9pZCwgaGFkbV9pZCwgc3RhcnR0aW1lKSAlPiUKICBtdXRhdGUoaXNNb3N0UmVjZW50TGFiRXZlbnQgPSBjaGFydHRpbWUubGFiID09IG1heChjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKGlzTW9zdFJlY2VudExhYkV2ZW50KSAgJT4lIGRpc3RpbmN0KCkgLT4gcHJlX2tfbGFiX3JlcGxldGlvbnNfTVZfbmV3CgpwcmVfa19sYWJfcmVwbGV0aW9uc19NVl9uZXcKYGBgCgpGb3IgdGhlIHBvc3QtcmVwbGV0aW9uIGxhYiB2YWx1ZXMsIHdlIGFyZSBub3cgZG9pbmcgdGhlIHNhbWUgdGhpbmcsIGJ1dCBpbnN0ZWFkIGZpbmRpbmcgdGhlIG1heGltdW0gZW5kdGltZSBwcmlvciB0byBhIGdpdmVuIGxhYiB2YWx1ZSwgdG8gc2VsZWN0IHRoZSBtb3N0IHJlY2VudCByZXBsZXRpb24gcHJpb3IgdG8gYSBnaXZlbiBsYWIgdmFsdWUuIAoKYGBge3J9CiNQb3Rhc3NpdW0gbGFiIGV2ZW50IFBPU1QtcmVwbGV0aW9ucyAKYWxsUmVwTGFiRXZlbnRzX2sgJT4lCiAgZmlsdGVyKGlzUmVjZW50UG9zdCkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsY2hhcnR0aW1lLmxhYikgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudFJlcGxldGlvbiA9IGVuZHRpbWUgPT0gbWF4KGVuZHRpbWUpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50UmVwbGV0aW9uKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsIGVuZHRpbWUpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRMYWJFdmVudCA9IGNoYXJ0dGltZS5sYWIgPT0gbWluKGNoYXJ0dGltZS5sYWIpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50TGFiRXZlbnQpICAlPiUgZGlzdGluY3QoKSAtPiBwb3N0X2tfbGFiX3JlcGxldGlvbnNfTVZfbmV3Cgpwb3N0X2tfbGFiX3JlcGxldGlvbnNfTVZfbmV3CmBgYAoKRG9pbmcgaXQgb24gdGhlIGNvbWJpbmVkIGRhdGFzZXQKYGBge3J9CmFsbF9lbGVjdHJvbHl0ZV9yZXBMYWJFdmVudHMgJT4lIAogIGZpbHRlcihpc1JlY2VudFByZSkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsY2hhcnR0aW1lLmxhYiwgRWxlY3Ryb2x5dGUpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRSZXBsZXRpb24gPSBzdGFydHRpbWUgPT0gbWluKHN0YXJ0dGltZSkpICU+JQogIGZpbHRlcihpc01vc3RSZWNlbnRSZXBsZXRpb24pICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgZ3JvdXBfYnkoc3ViamVjdF9pZCwgaGFkbV9pZCwgc3RhcnR0aW1lLCBFbGVjdHJvbHl0ZSkgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudExhYkV2ZW50ID0gY2hhcnR0aW1lLmxhYiA9PSBtYXgoY2hhcnR0aW1lLmxhYikpICU+JQogIGZpbHRlcihpc01vc3RSZWNlbnRMYWJFdmVudCkgICU+JSBkaXN0aW5jdCgpIC0+IHByZV9sYWJfcmVwbGV0aW9uc19NVl9ORVdfYWxsRXMKCnByZV9sYWJfcmVwbGV0aW9uc19NVl9ORVdfYWxsRXMKCmFsbF9lbGVjdHJvbHl0ZV9yZXBMYWJFdmVudHMgJT4lCiAgZmlsdGVyKGlzUmVjZW50UG9zdCkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsY2hhcnR0aW1lLmxhYixFbGVjdHJvbHl0ZSkgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudFJlcGxldGlvbiA9IGVuZHRpbWUgPT0gbWF4KGVuZHRpbWUpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50UmVwbGV0aW9uKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsIGVuZHRpbWUsRWxlY3Ryb2x5dGUpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRMYWJFdmVudCA9IGNoYXJ0dGltZS5sYWIgPT0gbWluKGNoYXJ0dGltZS5sYWIpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50TGFiRXZlbnQpICAlPiUgZGlzdGluY3QoKSAtPiBwb3N0X2xhYl9yZXBsZXRpb25zX01WX05FV19hbGxFcwoKcG9zdF9sYWJfcmVwbGV0aW9uc19NVl9ORVdfYWxsRXMKCmFsbEVzX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3IDwtIGJpbmRfcm93cyhsaXN0KHByZV9yZXBsZXRpb24gPSBwcmVfbGFiX3JlcGxldGlvbnNfTVZfTkVXX2FsbEVzLCBwb3N0X3JlcGxldGlvbiA9IHBvc3RfbGFiX3JlcGxldGlvbnNfTVZfTkVXX2FsbEVzKSwgLmlkID0gInByZVZzUG9zdCIpCgphbGxFc19wcmVfcG9zdF9yZXBsZXRpb25zX01WX25ldyAlPiUKICAgc2VsZWN0KGxpbmtvcmRlcmlkLCBFbGVjdHJvbHl0ZSkgJT4lCiAgZGlzdGluY3QoKSU+JQogIGdyb3VwX2J5KEVsZWN0cm9seXRlKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSkKYGBgCgoKCldlIHRoZW4gY29tYmluZSBhbGwgb2YgdGhlIHZhbHVlcyBmcm9tIHRoZSAicHJlLXJlcGxldGlvbiIgYW5kICJwb3N0LXJlcGxldGlvbiIgdGFibGVzLCBpbnRvIG9uZSB0YWJsZS4gV2UgY3JlYXRlIGEgbmV3IGNvbHVtbiBgcHJlVnNQb3N0YCB0byBpbmRpY2F0ZSBpZiBhIGdpdmVuIGV2ZW50IGlzIGVpdGhlciBiZWZvcmUgb3IgYWZ0ZXIgYSBsYWIgdmFsdWUuIENvbmNlaXZhYmx5LCB0aGUgc2FtZSByZXBsZXRpb24gY291bGQgY29ycmVzcG9uZCB0byBhIAoKYGBge3J9CiNDb21iaW5lIHRoZW0gaW50byBvbmUgZGF0YXNldCAKa19wcmVfcG9zdF9yZXBsZXRpb25zX01WX25ldyA8LSBiaW5kX3Jvd3MobGlzdChwcmVfcmVwbGV0aW9uID0gcHJlX2tfbGFiX3JlcGxldGlvbnNfTVZfbmV3LCBwb3N0X3JlcGxldGlvbiA9IHBvc3Rfa19sYWJfcmVwbGV0aW9uc19NVl9uZXcpLCAuaWQgPSAicHJlVnNQb3N0IikKa19wcmVfcG9zdF9yZXBsZXRpb25zX01WX25ldyAlPiUgZGlzdGluY3QoKQoKI1RoaXMgZ2l2ZXMgdGhlIGFtb3VudCBvZiByZXBsZXRpb25zIGV2ZW50cyB3aXRoIHRoZSBzYW1lIGxpbmtvcmRlciBpZC4gQW55dGhpbmcgb3ZlciAyIG1pZ2h0IG5lZWQgdG8gYmUgZXhsY3VkZWQsIGFzIHRoaXMgc2hvdWxkIGhhdmUgYmVlbiBicm9rZW4gdXAuIEl0IHJlZmxlY3RzIHRoZSBpbmhlcmVudCBlcnJvciBpbiBFSFJzIGl0IHNlZW1zIGxpa2UuIAprX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3ICAlPiUKICBncm91cF9ieShsaW5rb3JkZXJpZCkgJT4lCiAgc3VtbWFyaXplKG9ic2VydmF0aW9ucyA9IG4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKG9ic2VydmF0aW9ucykpICU+JQogIGNvdW50KG9ic2VydmF0aW9ucykKCiNGaWx0ZXJzIGFsbCBvZiB0aGUgcm93cyB3aGVyZSB0aGVyZSBpcyBhIHBvc3QgYW5kIGEgcHJlLXJlcGxldGlvbiB2YWx1ZSAoaG9wZWZ1bGx5IGxvbCkgCmtfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lCiAgZ3JvdXBfYnkobGlua29yZGVyaWQpICU+JSAKICBmaWx0ZXIobigpID09IDEpICU+JQogIHVuZ3JvdXAoKSU+JSAKICBncm91cF9ieShwcmVWc1Bvc3QpICU+JQogIHN1bW1hcml6ZSggbWVhbiA9IG1lYW4odmFsdWVudW0sIG5hLnJtID0gVCksIG9icyA9IG4oKSwgc2QgPSBzZCh2YWx1ZW51bSwgbmEucm0gPSBUKSkKCmtfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lCiAgZ3JvdXBfYnkobGlua29yZGVyaWQpICU+JSAKICBmaWx0ZXIobigpID09IDIpICU+JQogIHVuZ3JvdXAoKSU+JSAKICBncm91cF9ieShwcmVWc1Bvc3QpICU+JQogIHN1bW1hcml6ZSggbWVhbiA9IG1lYW4odmFsdWVudW0sIG5hLnJtID0gVCksIG9icyA9IG4oKSwgc2QgPSBzZCh2YWx1ZW51bSwgbmEucm0gPSBUKSkKICAKI1RoaXMgZ2l2ZXMgdGhlIGZyZXF1ZW5jeSBmb3IgdGhlIGFtb3VudCBvZiB1bmlxdWUgdmFsdWVzIAprX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3ICAlPiUKICBncm91cF9ieShjaGFydHRpbWUubGFiKSAlPiUKICBzdW1tYXJpemUob2JzZXJ2YXRpb25zID0gbigpKSAlPiUKICBhcnJhbmdlKGRlc2Mob2JzZXJ2YXRpb25zKSkgJT4lCiAgY291bnQob2JzZXJ2YXRpb25zKQoKCmBgYAoKIyMjIyBNYWduZXNpdW0KYGBge3J9CmFsbFJlcExhYkV2ZW50c19tZyAlPiUKICBmaWx0ZXIoaXNSZWNlbnRQcmUpICU+JSAKICBncm91cF9ieShzdWJqZWN0X2lkLCBoYWRtX2lkLGNoYXJ0dGltZS5sYWIpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRSZXBsZXRpb24gPSBzdGFydHRpbWUgPT0gbWluKHN0YXJ0dGltZSkpICU+JQogIGZpbHRlcihpc01vc3RSZWNlbnRSZXBsZXRpb24pICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgZ3JvdXBfYnkoc3ViamVjdF9pZCwgaGFkbV9pZCwgc3RhcnR0aW1lKSAlPiUKICBtdXRhdGUoaXNNb3N0UmVjZW50TGFiRXZlbnQgPSBjaGFydHRpbWUubGFiID09IG1heChjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKGlzTW9zdFJlY2VudExhYkV2ZW50KSAgJT4lIGRpc3RpbmN0KCkgLT4gcHJlX21nX2xhYl9yZXBsZXRpb25zX01WX25ldwoKcHJlX21nX2xhYl9yZXBsZXRpb25zX01WX25ldwoKI01hZ25lc2l1bSBsYWIgZXZlbnQgUE9TVC1yZXBsZXRpb25zIAphbGxSZXBMYWJFdmVudHNfbWcgJT4lCiAgZmlsdGVyKGlzUmVjZW50UG9zdCkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsY2hhcnR0aW1lLmxhYikgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudFJlcGxldGlvbiA9IGVuZHRpbWUgPT0gbWF4KGVuZHRpbWUpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50UmVwbGV0aW9uKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsIGVuZHRpbWUpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRMYWJFdmVudCA9IGNoYXJ0dGltZS5sYWIgPT0gbWluKGNoYXJ0dGltZS5sYWIpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50TGFiRXZlbnQpICAlPiUgZGlzdGluY3QoKSAtPiBwb3N0X21nX2xhYl9yZXBsZXRpb25zX01WX25ldwoKcG9zdF9tZ19sYWJfcmVwbGV0aW9uc19NVl9uZXcKCiNDb21iaW5lIHRoZW0gaW50byBvbmUgZGF0YXNldCAKbWdfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgPC0gYmluZF9yb3dzKGxpc3QocHJlX3JlcGxldGlvbiA9IHByZV9tZ19sYWJfcmVwbGV0aW9uc19NVl9uZXcsIHBvc3RfcmVwbGV0aW9uID0gcG9zdF9tZ19sYWJfcmVwbGV0aW9uc19NVl9uZXcpLCAuaWQgPSAicHJlVnNQb3N0IikKbWdfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lIGRpc3RpbmN0KCkKYGBgCgojIyMjIENhbGNpdW0KYGBge3J9CiNDYWxjaXVtIGxhYiBldmVudCBQUkUtcmVwbGV0aW9ucyAKYWxsUmVwTGFiRXZlbnRzX2NhICU+JQogIGZpbHRlcihpc1JlY2VudFByZSkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsY2hhcnR0aW1lLmxhYikgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudFJlcGxldGlvbiA9IHN0YXJ0dGltZSA9PSBtaW4oc3RhcnR0aW1lKSkgJT4lCiAgZmlsdGVyKGlzTW9zdFJlY2VudFJlcGxldGlvbikgJT4lCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShzdWJqZWN0X2lkLCBoYWRtX2lkLCBzdGFydHRpbWUpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRMYWJFdmVudCA9IGNoYXJ0dGltZS5sYWIgPT0gbWF4KGNoYXJ0dGltZS5sYWIpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50TGFiRXZlbnQpICAlPiUgZGlzdGluY3QoKSAtPiBwcmVfY2FfbGFiX3JlcGxldGlvbnNfTVZfbmV3CgpwcmVfY2FfbGFiX3JlcGxldGlvbnNfTVZfbmV3CgojQ2FsY2l1bSBsYWIgZXZlbnQgUE9TVC1yZXBsZXRpb25zIAphbGxSZXBMYWJFdmVudHNfY2EgJT4lCiAgZmlsdGVyKGlzUmVjZW50UG9zdCkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsY2hhcnR0aW1lLmxhYikgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudFJlcGxldGlvbiA9IGVuZHRpbWUgPT0gbWF4KGVuZHRpbWUpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50UmVwbGV0aW9uKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsIGVuZHRpbWUpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRMYWJFdmVudCA9IGNoYXJ0dGltZS5sYWIgPT0gbWluKGNoYXJ0dGltZS5sYWIpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50TGFiRXZlbnQpICAlPiUgZGlzdGluY3QoKSAtPiBwb3N0X2NhX2xhYl9yZXBsZXRpb25zX01WX25ldwoKcG9zdF9jYV9sYWJfcmVwbGV0aW9uc19NVl9uZXcKCiNDb21iaW5lIHRoZW0gaW50byBvbmUgZGF0YXNldCAKY2FfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgPC0gYmluZF9yb3dzKGxpc3QocHJlX3JlcGxldGlvbiA9IHByZV9jYV9sYWJfcmVwbGV0aW9uc19NVl9uZXcsIHBvc3RfcmVwbGV0aW9uID0gcG9zdF9jYV9sYWJfcmVwbGV0aW9uc19NVl9uZXcpLCAuaWQgPSAicHJlVnNQb3N0IikKY2FfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lIGRpc3RpbmN0KCkKCmBgYAoKIyMjIyBQaG9zcGhhdGUKYGBge3J9CiNQaG9zcGhhdGUgbGFiIGV2ZW50IFBSRS1yZXBsZXRpb25zIAphbGxSZXBMYWJFdmVudHNfcGggJT4lCiAgZmlsdGVyKGlzUmVjZW50UHJlKSAlPiUgCiAgZ3JvdXBfYnkoc3ViamVjdF9pZCwgaGFkbV9pZCxjaGFydHRpbWUubGFiKSAlPiUKICBtdXRhdGUoaXNNb3N0UmVjZW50UmVwbGV0aW9uID0gc3RhcnR0aW1lID09IG1pbihzdGFydHRpbWUpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50UmVwbGV0aW9uKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsIHN0YXJ0dGltZSkgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudExhYkV2ZW50ID0gY2hhcnR0aW1lLmxhYiA9PSBtYXgoY2hhcnR0aW1lLmxhYikpICU+JQogIGZpbHRlcihpc01vc3RSZWNlbnRMYWJFdmVudCkgICU+JSBkaXN0aW5jdCgpIC0+IHByZV9waF9sYWJfcmVwbGV0aW9uc19NVl9uZXcKCnByZV9waF9sYWJfcmVwbGV0aW9uc19NVl9uZXcKCiNQaG9zcGhhdGUgbGFiIGV2ZW50IFBPU1QtcmVwbGV0aW9ucyAKYWxsUmVwTGFiRXZlbnRzX3BoICU+JQogIGZpbHRlcihpc1JlY2VudFBvc3QpICU+JSAKICBncm91cF9ieShzdWJqZWN0X2lkLCBoYWRtX2lkLGNoYXJ0dGltZS5sYWIpICU+JQogIG11dGF0ZShpc01vc3RSZWNlbnRSZXBsZXRpb24gPSBlbmR0aW1lID09IG1heChlbmR0aW1lKSkgJT4lCiAgZmlsdGVyKGlzTW9zdFJlY2VudFJlcGxldGlvbikgJT4lCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShzdWJqZWN0X2lkLCBoYWRtX2lkLCBlbmR0aW1lKSAlPiUKICBtdXRhdGUoaXNNb3N0UmVjZW50TGFiRXZlbnQgPSBjaGFydHRpbWUubGFiID09IG1pbihjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKGlzTW9zdFJlY2VudExhYkV2ZW50KSAgJT4lIGRpc3RpbmN0KCkgLT4gcG9zdF9waF9sYWJfcmVwbGV0aW9uc19NVl9uZXcKCnBvc3RfcGhfbGFiX3JlcGxldGlvbnNfTVZfbmV3CgojQ29tYmluZSB0aGVtIGludG8gb25lIGRhdGFzZXQgCnBoX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3IDwtIGJpbmRfcm93cyhsaXN0KHByZV9yZXBsZXRpb24gPSBwcmVfcGhfbGFiX3JlcGxldGlvbnNfTVZfbmV3LCBwb3N0X3JlcGxldGlvbiA9IHBvc3RfcGhfbGFiX3JlcGxldGlvbnNfTVZfbmV3KSwgLmlkID0gInByZVZzUG9zdCIpCnBoX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3ICU+JSBkaXN0aW5jdCgpCgpgYGAKCiNDb21iaW5lIGFsbCBkYXRhc2V0cyBpbnRvIG9uZQpXZSdyZSBnb2luZyB0byBtYWtlIG9uZSBnaWFudCBkYXRhc2V0IGFjcm9zcyBhbGwgb2YgdGhlIGVsZWN0cm9seXRlcy4gCgpgYGB7cn0KYWxsX2x5dGVzX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3IDwtIGJpbmRfcm93cyhsaXN0KCBwb3Rhc3NpdW0gPSBrX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3LCBtYWduZXNpdW0gPSBtZ19wcmVfcG9zdF9yZXBsZXRpb25zX01WX25ldywgY2FsY2l1bSA9IGNhX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3LCBwaG9zcGhhdGUgPSBwaF9wcmVfcG9zdF9yZXBsZXRpb25zX01WX25ldyksIC5pZCA9ICJFbGVjdHJvbHl0ZSIpCgphbGxfbHl0ZXNfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lCiAgc2VsZWN0KGxpbmtvcmRlcmlkLCBFbGVjdHJvbHl0ZSkgJT4lCiAgZGlzdGluY3QoKSU+JQogIGdyb3VwX2J5KEVsZWN0cm9seXRlKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSkKCmFsbEVzX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3ICU+JQogICBzZWxlY3QobGlua29yZGVyaWQsIEVsZWN0cm9seXRlKSAlPiUKICBkaXN0aW5jdCgpJT4lCiAgZ3JvdXBfYnkoRWxlY3Ryb2x5dGUpICU+JQogIHN1bW1hcml6ZShuID0gbigpKQpgYGAKIyMgRXhjbHVzaW9ucyAKV2Ugbm93IHdhbnQgdG8gZXhjbHVkZSBhbGwgdGhlIHJvd3MgYmFzZWQgb24gY2VydGFpbiBjcml0ZXJpYS4gCgpXZSBoYXZlIGEgdGFibGUgY2FsbGVkIGBoYWRtX2lkX3RhYmxlYCB3aGljaCBjb250YWlucyBhbGwgb2YgdGhlIGhhZG1faWRzIG9mIHBhdGllbnRzIHdpdGggZGlhZ25vc2VzIHRoYXQgd291bGQgY29uZm91bmQgb3VyIHJlc3VsdHMgKGtpZG5leSBkaXNlYXNlLCBldGMpLiAoV0lMTCBBREQgSE9XIEkgQ1JFQVRFRCBUSElTIFRBQkxFIExBVEVSKS4KCmBgYHtyfQpoYWRtX2lkX3RhYmxlCmBgYAojIyBFeGFtaW5pbmcgYW1vdW50IG9mIGV4Y2x1c2lvbnMKRmlyc3QgYnViYmxlIG9mIHNjaGVtdGljCmBgYHtyfQojIyBUb3RhbCB2aXNpdHMgKGljdV9zdGF5cykgYmVmb3JlIGFueSBleGNsdXNpb25zIChQdC4xIG9mIHNjaGVtYXRpYykKYWxsX3JlcGxldGlvbnNfTVZfbmV3X0FFcyAlPiUgCiAgdW5ncm91cCgpICU+JQogIHNlbGVjdChpY3VzdGF5X2lkKSAlPiUgCiAgZGlzdGluY3QoKSAlPiUKICBjb3VudCgpCgojVGhlIGFtb3VudCBvZiBkaXN0aW5jdCBJY3UgU3RheXMgYWNyb3NzIGVhY2ggZWxlY3Ryb2x5dGUgCmFsbF9yZXBsZXRpb25zX01WX25ld19BRXMgJT4lCiAgdW5ncm91cCgpJT4lCiAgc2VsZWN0KGljdXN0YXlfaWQsIEVsZWN0cm9seXRlKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGdyb3VwX2J5KEVsZWN0cm9seXRlKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiAga2FibGUoKQoKI1RoZSBhbW91bnQgb2YgZGlzdGluY3QgcmVwbGV0aW9ucyBhY3Jvc3MgZWFjaCBlbGVjdHJvbHl0ZSAKYWxsX3JlcGxldGlvbnNfTVZfbmV3X0FFcyAlPiUKICB1bmdyb3VwKCklPiUKICBzZWxlY3QobGlua29yZGVyaWQsIEVsZWN0cm9seXRlKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGdyb3VwX2J5KEVsZWN0cm9seXRlKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiAga2FibGUoKQpgYGAKCgoKCgpUaGlyZCBidWJibGUgb2Ygc2NoZW1hdGljOiBUaG9zZSB0aGF0IGhhdmUgYmVlbiBleGNsdWRlZCBwcmUgYW5kIHBvc3QgMjQgaHJzLCBhbmQgYWxzbyB0aGUgbW9zdCByZWNlbnQgbGFiIHZhbHVlIGJlZm9yZSBhbmQgYWZ0ZXIgZWFjaCByZXBsZXRpb24KYGBge3J9CmFsbEVzX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3ICU+JSAjQW1vdW50IG9mIERJU1RJTkNUIElDVSB2aXNpdHMgCiAgdW5ncm91cCgpJT4lCiAgc2VsZWN0KGljdXN0YXlfaWQpICU+JSAKICBkaXN0aW5jdCgpICU+JQogIGNvdW50KCkKCmFsbEVzX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3ICU+JSAjQW1vdW50IG9mIERJU1RJTkNUIFJlcGxldGlvbiBFdmVudHMgCiAgdW5ncm91cCgpJT4lCiAgc2VsZWN0KGxpbmtvcmRlcmlkLCBFbGVjdHJvbHl0ZSkgJT4lCiAgZGlzdGluY3QoKSAlPiUKICBncm91cF9ieShFbGVjdHJvbHl0ZSkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCkpICU+JSAKICBhcnJhbmdlKGRlc2MobikpICU+JQogIGthYmxlKCkKCiNFeGNsdXNpb25zIG9uIHZpc2l0cwphbGxFc19wcmVfcG9zdF9yZXBsZXRpb25zX01WX25ldyAlPiUKICB1bmdyb3VwKCklPiUKICBpbm5lcl9qb2luKGhhZG1faWRfdGFibGUsIGJ5ID0gImhhZG1faWQiKSAlPiUgCiAgc2VsZWN0KGljdXN0YXlfaWQsIC5pZCkgJT4lCiAgZGlzdGluY3QoKSAlPiUKICBncm91cF9ieSggLmlkKSAlPiUKICBzdW1tYXJpemUob2JzID0gbigpKQoKI0Ftb3VudCBvZiBkaXN0aW5jdCBJQ1UgdmlzaXRzIGxlZnQKYWxsRXNfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lCiAgdW5ncm91cCgpJT4lCiAgYW50aV9qb2luKGhhZG1faWRfdGFibGUsIGJ5ID0gImhhZG1faWQiKSAlPiUgCiAgc2VsZWN0KGljdXN0YXlfaWQpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgY291bnQoKQoKYWxsRXNfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lICNBbW91bnQgb2YgRElTVElOQ1QgUmVwbGV0aW9uIEV2ZW50cyBsZWZ0IAogIHVuZ3JvdXAoKSU+JQogIGFudGlfam9pbihoYWRtX2lkX3RhYmxlLCBieSA9ICJoYWRtX2lkIikgJT4lIAogIHNlbGVjdChsaW5rb3JkZXJpZCwgRWxlY3Ryb2x5dGUpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgZ3JvdXBfYnkoRWxlY3Ryb2x5dGUpICU+JQogIHN1bW1hcml6ZShuID0gbigpKSAlPiUgCiAgYXJyYW5nZShkZXNjKG4pKSAlPiUKICBrYWJsZSgpCmBgYAoKCkFzIGEgcmVzdWx0LCB3ZSBjb3VsZCBzaW1wbHkgZG8gYSBzZW1pLWpvaW4gdG8gZGV0ZXJtaW5lIGhvdyBtYW55IHBlb3BsZSBpbiBlYWNoIGdyb3VwIHdlcmUgZXhjbHVkZWQuICoqTm90ZSoqLCB3ZSBhcmUgZG9pbmcgdGhlIGFudGktam9pbiBvbiB0aGUgdGFibGUgd2l0aCB0aGUgbW9zdCByZWNlbnQgcmVwbGV0aW9uIGJlZm9yZSBhbmQgYWZ0ZXIgYSBnaXZlbiBsYWIgcmVzdWx0LCB3aXRoaW4gYSAyNCBob3VyIHRpbWUgcmFuZ2UuIFdlIGFyZSAqKm5vdCoqIGRvaW5nIGl0IG9uIHRoZSBzZXQgb2YgQUxMIHBvc3NpYmxlIHJlcGxldGlvbiBhbmQgb3IgbGFiIHJlc3VsdCBldmVudHMuIAoKYGBge3J9CmhhZG1faWRfdGFibGUgJT4lCiAgc2VtaV9qb2luKGFsbF9seXRlc19wcmVfcG9zdF9yZXBsZXRpb25zX01WX25ldywgYnkgPSAiaGFkbV9pZCIpICU+JSAKICBzZWxlY3QoaGFkbV9pZCwgLmlkKSAlPiUKICBncm91cF9ieSguaWQpICU+JQogIHN1bW1hcml6ZShuID0gbigpKSAlPiUgCiAgYXJyYW5nZShkZXNjKG4pKSAlPiUKICBrYWJsZSgpCgojIyBOdW1iZXIgb2YgaG9zcGl0YWwgdmlzaXRzIGZvciBwb3Rhc3NpdW0KaGFkbV9pZF90YWJsZSAlPiUKICAjIHNlbWlfam9pbihrX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3LCBieSA9ICJoYWRtX2lkIikgJT4lCiAgaW5uZXJfam9pbihrX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3LCBieSA9ICJoYWRtX2lkIikgJT4lCiAgc2VsZWN0KGhhZG1faWQsIC5pZCkgJT4lCiAgZ3JvdXBfYnkoLmlkKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiAga2FibGUoKQoKa19wcmVfcG9zdF9yZXBsZXRpb25zX01WX25ldyAlPiUKICB1bmdyb3VwKCklPiUKICBzZW1pX2pvaW4oaGFkbV9pZF90YWJsZSwgYnkgPSAiaGFkbV9pZCIpICU+JSAKICBzZWxlY3QoaWN1c3RheV9pZCwgb3JkZXJpZCwgLmlkKSAlPiUKICBncm91cF9ieSguaWQpICU+JQogIHN1bW1hcml6ZShuID0gbigpKSAlPiUgCiAgYXJyYW5nZShkZXNjKG4pKSAlPiUKICBrYWJsZSgpCgoKYGBgCgpBbmQgaGVyZSBpcyB0aGUgcmVzdWx0YW50IGRhdGFzZXQgYWZ0ZXIgKmFjdHVhbGx5KiBleGNsdWRpbmcgdGhpcyB0aW1lLCB1c2luZyBhbiBhbnRpLWpvaW4gaW5zdGVhZC4gCmBgYHtyfQprX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3ICU+JQogIGFudGlfam9pbihoYWRtX2lkX3RhYmxlLCBieSA9ICJoYWRtX2lkIikgLT4gcG9zdEV4Y2x1c2lvbnNLX1JlcGxldGVkCgpwb3N0RXhjbHVzaW9uc0tfUmVwbGV0ZWQKYGBgCgojIyNBbm90aGVyIEV4Y2x1c2lvbnMgCmBgYHtyfQprX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3ICU+JQogIHNlbWlfam9pbihoYWRtX2lkX3RhYmxlLCBieSA9ICJoYWRtX2lkIikKCmhhZG1faWRfdGFibGUgJT4lCiAgc2VtaV9qb2luKGtfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcsIGJ5ID0gImhhZG1faWQiKQpgYGAKCgojIyBOb24gUmVwbGV0aW9uIEV2ZW50cwpXZSB3aWxsIG5vdyBkZXRlcm1pbmUgdGhlIHNldCBvZiBsYWIgdmFsdWVzIGZvciB3aGljaCB0aGVyZSB3YXMgbm8gaW1tZWRpYXRlIHJlcGxldGlvbiBhZnRlcndhcmQuIAoKIyMjIEV4dHJhY3Qgbm9uLXJlcGxldGVkIHZhbHVlcyB3aXRoaW4gMjQgaG91cnMKSW4gdGhpcyBzZWN0aW9uLCB3ZSBoYXZlIHN0YXJ0ZWQgYnkgZmlsdGVyaW5nIGFsbCB0aGUgcmVwbGV0aW9ucyBwcmlvciBhbmQgYWZ0ZXIgYSBsYWIgdmFsdWUsIGp1c3QgbGlrZSBmb3IgdGhlIGFuYWx5c2lzIG9uIHRoZSByZXBsZXRpb25zLiBIb3dldmVyLCB0aGlzIHRpbWUsIGlmIHRoZXJlIGFyZSBtdWx0aXBsZSBsYWIgdmFsdWVzIHRoYXQgb2NjdXIgYmVmb3JlIGEgZ2l2ZW4gcmVwbGV0aW9uIChvciBhZnRlciksIHdlIGFyZSB0YWtpbmcgdGhlIG9uZXMgYWZ0ZXIgdGhlIGZpcnN0IG9uZS4gCgpgYGB7cn0KI0FsbCBlbGVjdHJvbHl0ZSBsYWIgZXZlbnQgUFJFLXJlcGxldGlvbnMgCmFsbF9lbGVjdHJvbHl0ZV9yZXBMYWJFdmVudHMgJT4lCiAgZmlsdGVyKGlzUmVjZW50UHJlKSAlPiUgCiAgZ3JvdXBfYnkoc3ViamVjdF9pZCwgaGFkbV9pZCxjaGFydHRpbWUubGFiLCBFbGVjdHJvbHl0ZSkgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudFJlcGxldGlvbiA9IHN0YXJ0dGltZSA9PSBtaW4oc3RhcnR0aW1lKSkgJT4lCiAgZmlsdGVyKGlzTW9zdFJlY2VudFJlcGxldGlvbikgJT4lCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShzdWJqZWN0X2lkLCBoYWRtX2lkLCBzdGFydHRpbWUsIEVsZWN0cm9seXRlKSAlPiUKICBtdXRhdGUoaXNNb3N0UmVjZW50TGFiRXZlbnQgPSBjaGFydHRpbWUubGFiID09IG1heChjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKCFpc01vc3RSZWNlbnRMYWJFdmVudCkgICU+JSBkaXN0aW5jdCgpIC0+IG5vblJlcGxldExhYnNQcmVfYWxsRXMKCiNBbGwgZWxlY3Ryb2x5dGUgbGFiIGV2ZW50IFBPU1QtcmVwbGV0aW9ucyAKYWxsX2VsZWN0cm9seXRlX3JlcExhYkV2ZW50cyAlPiUKICBmaWx0ZXIoaXNSZWNlbnRQb3N0KSAlPiUgCiAgZ3JvdXBfYnkoc3ViamVjdF9pZCwgaGFkbV9pZCxjaGFydHRpbWUubGFiLCBFbGVjdHJvbHl0ZSkgJT4lCiAgbXV0YXRlKGlzTW9zdFJlY2VudFJlcGxldGlvbiA9IGVuZHRpbWUgPT0gbWF4KGVuZHRpbWUpKSAlPiUKICBmaWx0ZXIoaXNNb3N0UmVjZW50UmVwbGV0aW9uKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KHN1YmplY3RfaWQsIGhhZG1faWQsIGVuZHRpbWUsIEVsZWN0cm9seXRlKSAlPiUKICBtdXRhdGUoaXNNb3N0UmVjZW50TGFiRXZlbnQgPSBjaGFydHRpbWUubGFiID09IG1pbihjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKCFpc01vc3RSZWNlbnRMYWJFdmVudCkgICU+JSBkaXN0aW5jdCgpIC0+IG5vblJlcGxldExhYnNQb3N0X2FsbEVzCgphbGxOb25SZXBsZXRpb25zX2FsbEVzIDwtIGJpbmRfcm93cyhsaXN0KHByZV9yZXBsZXRpb24gPSBub25SZXBsZXRMYWJzUHJlX2FsbEVzLCBwb3N0X3JlcGxldGlvbiA9IG5vblJlcGxldExhYnNQb3N0X2FsbEVzKSwgLmlkID0gInByZVZzUG9zdCIpCgpgYGAKIyMjIyBUYWJsZQpgYGB7cn0KYWxsTm9uUmVwbGV0aW9uc19hbGxFcyAlPiUKICBncm91cF9ieShwcmVWc1Bvc3QsIEVsZWN0cm9seXRlKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSwgbWVhbiA9IG1lYW4odmFsdWVudW0sIG5hLnJtID0gVFJVRSksIHN0YW5kYXJkX2RldmlhdGlvbiA9IHNkKHZhbHVlbnVtLCBuYS5ybSA9IFQpKQpgYGAKIyMjIyBWaXN1YWxpemF0aW9ucwpgYGB7cn0KCmBgYAoKIyMgRXhjbHVzaW9ucyAKV2Ugbm93IHdhbnQgdG8gZXhjbHVkZSBhbGwgdGhlIHJvd3MgYmFzZWQgb24gY2VydGFpbiBjcml0ZXJpYS4gCgpXZSBoYXZlIGEgdGFibGUgY2FsbGVkIGBoYWRtX2lkX3RhYmxlYCB3aGljaCBjb250YWlucyBhbGwgb2YgdGhlIGhhZG1faWRzIG9mIHBhdGllbnRzIHdpdGggZGlhZ25vc2VzIHRoYXQgd291bGQgY29uZm91bmQgb3VyIHJlc3VsdHMgKGtpZG5leSBkaXNlYXNlLCBldGMpLiAoV0lMTCBBREQgSE9XIEkgQ1JFQVRFRCBUSElTIFRBQkxFIExBVEVSKS4KCmBgYHtyfQpoYWRtX2lkX3RhYmxlCmBgYAoKQXMgYSByZXN1bHQsIHdlIGNvdWxkIHNpbXBseSBkbyBhIHNlbWktam9pbiB0byBkZXRlcm1pbmUgaG93IG1hbnkgcGVvcGxlIGluIGVhY2ggZ3JvdXAgd2VyZSBleGNsdWRlZC4gKipOb3RlKiosIHdlIGFyZSBkb2luZyB0aGUgYW50aS1qb2luIG9uIHRoZSB0YWJsZSB3aXRoIHRoZSBtb3N0IHJlY2VudCByZXBsZXRpb24gYmVmb3JlIGFuZCBhZnRlciBhIGdpdmVuIGxhYiByZXN1bHQsIHdpdGhpbiBhIDI0IGhvdXIgdGltZSByYW5nZS4gV2UgYXJlICoqbm90KiogZG9pbmcgaXQgb24gdGhlIHNldCBvZiBBTEwgcG9zc2libGUgcmVwbGV0aW9uIGFuZCBvciBsYWIgcmVzdWx0IGV2ZW50cy4gCgpgYGB7cn0KaGFkbV9pZF90YWJsZSAlPiUKICBzZW1pX2pvaW4oYWxsX2x5dGVzX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3LCBieSA9ICJoYWRtX2lkIikgJT4lIAogIHNlbGVjdChoYWRtX2lkLCAuaWQpICU+JQogIGdyb3VwX2J5KC5pZCkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCkpICU+JSAKICBhcnJhbmdlKGRlc2MobikpICU+JQogIGthYmxlKCkKCiMjIE51bWJlciBvZiBob3NwaXRhbCB2aXNpdHMgZm9yIHBvdGFzc2l1bQpoYWRtX2lkX3RhYmxlICU+JQogICMgc2VtaV9qb2luKGtfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcsIGJ5ID0gImhhZG1faWQiKSAlPiUKICBpbm5lcl9qb2luKGtfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcsIGJ5ID0gImhhZG1faWQiKSAlPiUKICBzZWxlY3QoaGFkbV9pZCwgLmlkKSAlPiUKICBncm91cF9ieSguaWQpICU+JQogIHN1bW1hcml6ZShuID0gbigpKSAlPiUgCiAgYXJyYW5nZShkZXNjKG4pKSAlPiUKICBrYWJsZSgpCgprX3ByZV9wb3N0X3JlcGxldGlvbnNfTVZfbmV3ICU+JQogIHVuZ3JvdXAoKSU+JQogIHNlbWlfam9pbihoYWRtX2lkX3RhYmxlLCBieSA9ICJoYWRtX2lkIikgJT4lIAogIHNlbGVjdChpY3VzdGF5X2lkLCBvcmRlcmlkLCAuaWQpICU+JQogIGdyb3VwX2J5KC5pZCkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCkpICU+JSAKICBhcnJhbmdlKGRlc2MobikpICU+JQogIGthYmxlKCkKCgpgYGAKCkFuZCBoZXJlIGlzIHRoZSByZXN1bHRhbnQgZGF0YXNldCBhZnRlciAqYWN0dWFsbHkqIGV4Y2x1ZGluZyB0aGlzIHRpbWUsIHVzaW5nIGFuIGFudGktam9pbiBpbnN0ZWFkLiAKYGBge3J9CmtfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lCiAgYW50aV9qb2luKGhhZG1faWRfdGFibGUsIGJ5ID0gImhhZG1faWQiKSAtPiBwb3N0RXhjbHVzaW9uc0tfUmVwbGV0ZWQKCnBvc3RFeGNsdXNpb25zS19SZXBsZXRlZApgYGAKCiMjI0Fub3RoZXIgRXhjbHVzaW9ucyAKYGBge3J9CmtfcHJlX3Bvc3RfcmVwbGV0aW9uc19NVl9uZXcgJT4lCiAgc2VtaV9qb2luKGhhZG1faWRfdGFibGUsIGJ5ID0gImhhZG1faWQiKQoKaGFkbV9pZF90YWJsZSAlPiUKICBzZW1pX2pvaW4oa19wcmVfcG9zdF9yZXBsZXRpb25zX01WX25ldywgYnkgPSAiaGFkbV9pZCIpCmBgYAoKCiMjIEFuYWx5c2lzIGFuZCBWaXN1YWxpemF0aW9uIApXZSBhcmUgZ29pbmcgdG8gZG8gYW5hbHlzaXMgaGVyZSBvbiB0aGUgZGF0YXNldCBjb250YWluaW5nIHJlcGxldGlvbnMuIFdlIGFyZSBtYWtpbmcgdGhpcyBkaXN0aW5jdGlvbiwgYXMgbGF0ZXIgb24sIHdlIHdpbGwgZG8gYW5hbHlzaXMgb24gbm9uLXJlcGxldGlvbnMuIAoKIyMjIEFuYWx5c2lzIG9uIFJlcGxldGlvbnMKIyMjIyBUYWJsZQpJbiB0aGlzIHRhYmxlIHdlIGFyZSBqdXN0IGRvaW5nIHNvbWUgc3VtbWFyeSBhbmFseXNpcyBvbiB0aGUgZGF0YXNldC4gCgpgYGB7cn0KcG9zdEV4Y2x1c2lvbnNLX1JlcGxldGVkICU+JQogIGdyb3VwX2J5KCBwcmVWc1Bvc3QpICU+JQogIHN1bW1hcml6ZShuID0gbigpLCBtZWFuID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUUlVFKSwgc3RhbmRhcmRfZGV2aWF0aW9uID0gc2QodmFsdWVudW0sIG5hLnJtID0gVCkpIApgYGAKCiMjIyMgSGlzdG9ncmFtCkhpc3RvZ3JhbSBvZiBQcmUgYW5kIFBvc3QgUmVwbGV0aW9ucwpgYGB7cixmaWcuc3ViY2FwPSAiUHJlIFZzIFBvc3QgUmVwbGV0aW9uIExhYiBWYWx1ZXMifQpnZ3BhcihnZ2hpc3RvZ3JhbShkYXRhID0gcG9zdEV4Y2x1c2lvbnNLX1JlcGxldGVkLCB4PSJ2YWx1ZW51bSIsIGZpbGwgPSAicHJlVnNQb3N0IiwgIGFkZD0ibWVhbiIsIHBhbGV0dGUgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiKSxhZGRfZGVuc2l0eSA9IEZBTFNFLCBiaW5zID0gNDAsIGdnZ3RoZW1lID0gdGhlbWVfcHVicigpLCB4bGFiID0gIkxhYiBWYWx1ZSIsIHRpdGxlID0gIlByZSBWcyBQb3N0IFJlcGxldGlvbiBMYWIgVmFsdWVzIC0gUG90YXNzaXVtIiwgeWxhYiA9ICJSZXBsZXRpb25zIikKICAgICAgLCB4bGltID0gYygyLDYpKQoKZ2dwbG90KHBvc3RFeGNsdXNpb25zS19SZXBsZXRlZCwgYWVzKHggPSB2YWx1ZW51bSxmaWxsPXByZVZzUG9zdCkpICsKICBnZW9tX2hpc3RvZ3JhbShhbHBoYT0wLjYsIHBvc2l0aW9uPSJpZGVudGl0eSIsYmlucz00MCwgY29sb3I9ImJsYWNrIikgKyBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygyLDYpICkgKyB0aGVtZV9wdWJyKCkgKyB4bGFiKCJQb3Rhc3NpdW0gVmFsdWUiKSArIHlsYWIoIk51bWJlciBvZiBPY2N1cnJlbmNlcyIpICsgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIiIsIGxhYmVscyA9IGMoIlBvc3QgcmVwbGV0aW9uIEsiLCAiUHJlIHJlcGxldGlvbiBLIikpIC0+IGgxX2sKCmgxX2sKCmBgYAoKIyMjIyBQaWUgQ2hhcnQgCgpgYGB7cn0KCiAgcG9zdEV4Y2x1c2lvbnNLX1JlcGxldGVkICU+JQogICAgIG11dGF0ZShyZXBsZXRpb25SYW5nZSA9IGNhc2Vfd2hlbih2YWx1ZW51bSA8IDMuNSAmIGZsYWcgPT0gImFibm9ybWFsIiB+ICJiZWxvdyBub3JtYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlbnVtID4gMy41ICYgZmxhZyA9PSAiYWJub3JtYWwiIH4gImFib3ZlIHJhbmdlIixUUlVFIH4gIndpdGhpbiByYW5nZSIpKSAtPiBwb3N0RXhjbHVzaW9uc0tfUmVwbGV0ZWQKCnBvc3RFeGNsdXNpb25zS19SZXBsZXRlZCAlPiUKICBmaWx0ZXIocmVwbGV0aW9uUmFuZ2UgPT0gImFib3ZlIHJhbmdlIikgJT4lIAogIGdyb3VwX2J5KHZhbHVlbnVtKSAlPiUKICBzdW1tYXJpemUob2JzZXJ2YXRpb25zID0gbigpKSAlPiUgCiAgIGFycmFuZ2UodmFsdWVudW0pIAoKCnBvc3RFeGNsdXNpb25fZnJlcSA8LSBwb3N0RXhjbHVzaW9uc0tfUmVwbGV0ZWQgJT4lCiAgZ3JvdXBfYnkocmVwbGV0aW9uUmFuZ2UpICU+JQogIHN1bW1hcml6ZShvYnNlcnZhdGlvbnMgPSBuKCkpICU+JQogIG11dGF0ZShwZXJjZW50YWdlID0gb2JzZXJ2YXRpb25zIC8gc3VtKG9ic2VydmF0aW9ucykgKiAxMDApIAoKZ2dwbG90KHBvc3RFeGNsdXNpb25fZnJlcSwgYWVzKCIiLCBwZXJjZW50YWdlLCBmaWxsID0gcmVwbGV0aW9uUmFuZ2UpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAxKSArCiAgY29vcmRfcG9sYXIodGhldGEgPSAieSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPWMoICIjQjNCM0IzIiwiIzI3NDA4QiIsIiNFRTc2MjEiKSxuYW1lID0gIiAiLAogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoICJBYm92ZSA1LjIiLCJCZWxvdyAzLjQiLCAiMy40LTUuMiIgKQogICAgICAgICAgICAgICAgICAgICkgKwogIHRoZW1lX3ZvaWQoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIikgIC0+IHBpZV9jaGFydF9rCgpwaWVfY2hhcnRfawoKYGBgCgoKCiMjIyBOb24gLSBSZXBsZXRpb24gQW5hbHlzaXMgCgpgYGB7cn0KYWxsTm9uUmVwbGV0aW9ucyA8LSBiaW5kX3Jvd3MobGlzdChwcmVfcmVwbGV0aW9uID0gbm9uUmVwbGV0TGFic1ByZSwgcG9zdF9yZXBsZXRpb24gPSBub25SZXBsZXRMYWJzUG9zdCksIC5pZCA9ICJwcmVWc1Bvc3QiKQoKI1J1biBFeGNsdXNpb25zCmFsbE5vblJlcGxldGlvbnMgJT4lCiAgYW50aV9qb2luKGhhZG1faWRfdGFibGUsIGJ5ID0gImhhZG1faWQiKSAtPiBwb3N0ZXhjbHVzaW9uX25vblJlcGxldGlvbnMKCnBvc3RleGNsdXNpb25fbm9uUmVwbGV0aW9ucwpgYGAKCgojIyMjIFRhYmxlIAoKYGBge3J9CnBvc3RleGNsdXNpb25fbm9uUmVwbGV0aW9ucyAlPiUKICBncm91cF9ieSggcHJlVnNQb3N0KSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSwgbWVhbiA9IG1lYW4odmFsdWVudW0sIG5hLnJtID0gVFJVRSksIHN0YW5kYXJkX2RldmlhdGlvbiA9IHNkKHZhbHVlbnVtLCBuYS5ybSA9IFQpKSAKYGBgCgoKIyMjIENvbWJpbmluZyB0aGUgcmVwbGV0ZWQgdnMgTm9uUmVwbGV0ZWQgcHJlIGxhYiB2YWx1ZXMgZm9yIGNvbXBhcmlzb24KYGBge3J9CnJlcGxldGVkVnNOb25SZXBsZXRlZF9QcmVWIDwtIGJpbmRfcm93cyhsaXN0KHJlcGxldGVkID0gcG9zdEV4Y2x1c2lvbnNLX1JlcGxldGVkLCBub25SZXBsZXRlZCA9IHBvc3RleGNsdXNpb25fbm9uUmVwbGV0aW9ucyksIC5pZCA9ICJyZXBsZXRlVnNOb24iKSAlPiUgZmlsdGVyKHByZVZzUG9zdCA9PSAicHJlX3JlcGxldGlvbiIpCnJlcGxldGVkVnNOb25SZXBsZXRlZF9QcmVWICU+JSBkaXN0aW5jdCgpCmBgYAojIyMjIFRhYmxlCmBgYHtyfQojVGFibGUKcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lCiAgZ3JvdXBfYnkocmVwbGV0ZVZzTm9uLGZsYWcpICU+JQogIHN1bW1hcml6ZShvYnNlcnZhdGlvbnMgPSBuKCksbWVhbiA9IG1lYW4odmFsdWVudW0sIG5hLnJtID0gVCksIHN0ZF9kZXYgPSBzZCh2YWx1ZW51bSwgbmEucm09VCkpICU+JQogIG11dGF0ZShwZXJjZW50YWdlID0gb2JzZXJ2YXRpb25zIC8gc3VtKG9ic2VydmF0aW9ucykgKiAxMDApIApgYGAKCgojIyMjIFZpc3VhbGl6YXRpb24KCmBgYHtyfQojVmlzdWFsaXppbmcgdGhlIG5vbi1yZXBsZXRlZCB2cyByZXBsZXRlZCBsYWIgdmFsdWVzIApnZ3BhcihnZ2hpc3RvZ3JhbShkYXRhID0gcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYsIHg9InZhbHVlbnVtIiwgZmlsbCA9ICJmbGFnIiwgYWRkPSJtZWFuIixwb3NpdGlvbiA9ICJpZGVudGl0eSIsIHBhbGV0dGUgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiKSxhZGRfZGVuc2l0eSA9IEZBTFNFLCBiaW5zID0gMTAwLCBnZ2d0aGVtZSA9IHRoZW1lX3B1YnIoKSwgeGxhYiA9ICJMYWIgVmFsdWUiLCB0aXRsZSA9ICJQcmUgVnMgUG9zdCBSZXBsZXRpb24gTGFiIFZhbHVlcyAtIFBvdGFzc2l1bSIsIHlsYWIgPSAiUmVwbGV0aW9ucyIsIGZhY2V0LmJ5ID0gInJlcGxldGVWc05vbiIpCiAgICAgICwgeGxpbSA9IGMoMiw2KSkKYGBgCioqUXVlc3Rpb24qKjogSG93IGNhbiB0aGVyZSBiZSBoaXN0b2dyYW0gYmFycyB0aGF0IGFyZSAiYWJub3JtYWwiIG9uIHRvcCBvZiBiYXJzIHRoYXQgYXJlIHdoaXRlPyBBcmUgdGhleSBqdXN0IG5vdCBncmFudWxhciBlbm91Z2ggdG8gZGlmZmVyZW50aWF0ZSB0aGVtPyAKSSB0aGluayB0aGVyZSBhcmUgZXJyb3JzIGluIGhvdyB0aGUgZGF0YSB3YXMgZmxhZ2dlZC4gU2hvdWxkIGxvb2sgbW9yZSBpbnRvIHRoaXMuIAojIyMjIExvb2tpbmcgYXQgdGhlIG91dGxpZXJzIAoKIyMjIyMgUmVwbGV0ZWQgYWJvdmUgdGhlIHRocmVzaG9sZC4KYGBge3J9CnJlcGxldGVkVnNOb25SZXBsZXRlZF9QcmVWICU+JQogIGZpbHRlcihyZXBsZXRlVnNOb24gPT0gInJlcGxldGVkIikgJT4lCiAgZmlsdGVyKGZsYWcgPT0gImFibm9ybWFsIiAmIHZhbHVlbnVtID4gNCkgJT4lCiAgdW5ncm91cCgpJT4lCiAgc3VtbWFyaXplKG9ic2VydmF0aW9ucyA9IG4oKSxtZWFuID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUKSwgc3RkX2RldiA9IHNkKHZhbHVlbnVtLCBuYS5ybT1UKSkgCmBgYAoKIyMjIyMgRGlkbid0IHJlcGxldGUgQmVsb3cgdGhlIHRocmVzaG9sZC4KCiMjIyMjIyBUYWJsZSAKYGBge3J9CnBvc3RleGNsdXNpb25fbm9uUmVwbGV0aW9ucyAlPiUKICBmaWx0ZXIoZmxhZyA9PSAiYWJub3JtYWwiICYgdmFsdWVudW0gPCA0KSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3VtbWFyaXplKG9ic2VydmF0aW9ucyA9IG4oKSxtZWFuID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUKSwgc3RkX2RldiA9IHNkKHZhbHVlbnVtLCBuYS5ybT1UKSkgCmBgYAoKIyMjIyMjIFZpc3VhbGl6YXRpb24gb2Ygbm90IHJlcGxldGlvbmcgYmVsb3cgdGhlIHRocmVzaG9sZAoKYGBge3J9CnBvc3RleGNsdXNpb25fbm9uUmVwbGV0aW9ucyAlPiUKICBmaWx0ZXIodmFsdWVudW0gPCAzLjUgJiBmbGFnID09ICJhYm5vcm1hbCIpICU+JQogIGdncGxvdChhZXMoeCA9IHZhbHVlbnVtKSkgKwogIGdlb21faGlzdG9ncmFtKGFscGhhPTAuNiwgcG9zaXRpb249ImlkZW50aXR5IixiaW5zPTE0LCBjb2xvcj0iYmxhY2siKSArIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDEsMy41KSkgKyB0aGVtZV9wdWJyKCkKYGBgCgoKIyMjQW1vdW50IG9mIHBlb3BsZSB0aGF0IHJlcGxldGUgKHZzIE5vbi1SZXBsZXRlKSBhY3Jvc3MgbGFiIHZhbHVlIAoKYGBge3J9CnJlcGxldGVkVnNOb25SZXBsZXRlZF9QcmVWICU+JQogIGdyb3VwX2J5KHZhbHVlbnVtLCByZXBsZXRlVnNOb24pICU+JQogIHN1bW1hcml6ZShvYnNlcnZhdGlvbnMgPSBuKCkgKSAlPiUKICBnZ3Bsb3QoYWVzKGZpbGw9cmVwbGV0ZVZzTm9uLCB5PW9ic2VydmF0aW9ucywgeD12YWx1ZW51bSkpICsgCiAgICBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIsIHN0YXQ9ImlkZW50aXR5IikgKyBzY2FsZV94X2NvbnRpbnVvdXMoKSAgKyB0aGVtZV9wdWJyKCkKYGBgCgojIyBUaW1lIG9mIERheSBBbmFseXNpcyAKRXhhbWluaW5nIHRoZSB0aW1lIG9mIGRheSBmb3IgZWFjaCByZXBsZXRpb24gYW5kIGxhYiB2YWx1ZS4gCgpgYGB7cn0KcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lCiAgbXV0YXRlKFJlcGxldGlvbkhvdXIgPSBob3VyKHN0YXJ0dGltZSksIExhYkhvdXIgPSBob3VyKGNoYXJ0dGltZS5sYWIpKSAlPiUKICBnZ2hpc3RvZ3JhbSh4ID0gYygiTGFiSG91ciIsIlJlcGxldGlvbkhvdXIiKSwgYmlucyA9IDI0LCBwYWxldHRlICA9ICJ1Y2hpY2FnbyIsIGFscGhhID0gMC41LCBtZXJnZSA9IFQsIHhsYWIgPSAiSG91cnMiLCB5bGFiID0gIk51bWJlciBvZiBPY2N1cmVuY2VzIikKCnJlcGxldGVkVnNOb25SZXBsZXRlZF9QcmVWICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoTGFiSG91ciA9IGhvdXIoY2hhcnR0aW1lLmxhYikgKyA0LCBSZXBsZXRpb25Ib3VyID0gaG91cihzdGFydHRpbWUpKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMoTGFiSG91cixSZXBsZXRpb25Ib3VyKSwgdmFsdWVzX3RvPSJIb3VyIiwgbmFtZXNfdG89IldoaWNoSG91ciIpIC0+IHl5CgpvcmRlcmVkKHl5JFdoaWNoSG91cikKeXkkV2hpY2hIb3VyIDwtIGZhY3Rvcih5eSRXaGljaEhvdXIsIG9yZGVyZWQgPSBGQUxTRSkKeXkkV2hpY2hIb3VyIDwtIHJlbGV2ZWwoeXkkV2hpY2hIb3VyLCJSZXBsZXRpb25Ib3VyIikKZ2dwbG90KHl5LCBhZXMoeCA9IEhvdXIsZmlsbD1XaGljaEhvdXIpKSArIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uPSJpZGVudGl0eSIsYmlucyA9IDI0LGFscGhhPTAuNikgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDI0LDIpKSAgKyB0aGVtZV9wdWJyKCkKCmdncGxvdCh5eSwgYWVzKHggPSBIb3VyLGZpbGw9V2hpY2hIb3VyKSkgKwogIGdlb21faGlzdG9ncmFtKGFscGhhPTAuNCwgcG9zaXRpb249ImlkZW50aXR5IixiaW5zPTI0LCBjb2xvcj0iYmxhY2siKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMjQsMikpICsgZmFjZXRfd3JhcCh2YXJzKHJlcGxldGVWc05vbikpCmBgYAoKIyMjIyBQcmUtUmVwbGV0aW9uIHZzIE5vbi1SZXBsZXRlZCBMYWIgVmFsdWVzIApCZWxvdywgd2UgaGF2ZSB0aGUgdGltZSBvZiBkYXkgZm9yIGxhYiB2YWx1ZSBkcmF3cyB0aGF0IG9jY3VyIGZvciBsYWIgdmFsdWVzIHRoYXQgd2VyZSByZXBsZXRlZCwgYW5kIHRob3NlIHRoYXQgZGlkIG5vdCBkaXJlY3RseSBsZWFkIHRvIGEgcmVwbGV0aW9uLiAKCkl0IHNlZW1zIHRoYXQgdGhvc2UgbGVhZGluZyB0byBhIHJlcGx0aW9uIHdlcmUgbW9yZSBsaWtlbHkgdG8gb2NjdXIgaW4gdGhlIG1vcm5pbmcsIHdoaWxlIHRob3NlIHRoYXQgZGlkIG5vdCBkaXJlY3RseSBsZWFkIHRvIGEgcmVwbGV0aW9uIHdlcmUgbW9yZSBsaWtlbHkgdG8gb2NjdXIgaW4gdGhlIGFmdGVybm9vbi4gCgpXaHkgaXMgdGhpcyBvY2N1cnJpbmc/IApQZXJoYXBzLCByZXBsZXRpb25zIGluIHRoZSBtb3JuaW5nIGFyZSAgcm91dGluZSwgYnV0IHRoZXJlIGlzIG5vIHJvdXRpbmUgaW4gcGVyZm9ybWluZyByZXBsZXRpb25zIGluIHRoZSBhZnRlcm5vb24uIApgYGB7cn0KcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShMYWJIb3VyID0gaG91cihjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKHByZVZzUG9zdCA9PSAicHJlX3JlcGxldGlvbiIpICU+JQogIGdncGxvdChhZXMoeCA9IExhYkhvdXIsZmlsbD1yZXBsZXRlVnNOb24pKSArIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uPSJpZGVudGl0eSIsYmlucyA9IDI0LGFscGhhPTAuNiwgY29sb3IgPSAiYmxhY2siKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMjQsMikpICArIHRoZW1lX3B1YnIoKSArIHhsYWIoIkhvdXIgb2YgRGF5IikgKyB5bGFiKCJOdW1iZXIgb2YgTGFiIFZhbHVlcyIpICsgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIiIsIGxhYmVscyA9IGMoIk5vbi1SZXBsZXRpb24iLCAiUmVwbGV0aW9uIikpCgoKYGBgCgojIyMjIEF2ZXJhZ2UgcHJlLXJlcGxldGlvbiBsYWIgdmFsdWUgYXQgZWFjaCBob3VyIAojIyMjIyBUYWJsZQpgYGB7cn0KcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShMYWJIb3VyID0gaG91cihjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKHByZVZzUG9zdCA9PSAicHJlX3JlcGxldGlvbiIpICU+JQogIGdyb3VwX2J5KExhYkhvdXIsIHJlcGxldGVWc05vbikgJT4lCiAgc3VtbWFyaXplKG1lYW5fbGFiX3ZhbHVlID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUKSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHJlcGxldGVWc05vbiwgdmFsdWVzX2Zyb20gPSBtZWFuX2xhYl92YWx1ZSkKYGBgCgoKIyMjIyMgVmlzdWFsaXphdGlvbgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShMYWJIb3VyID0gaG91cihjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKHByZVZzUG9zdCA9PSAicHJlX3JlcGxldGlvbiIpICU+JQogIGdyb3VwX2J5KExhYkhvdXIsIHJlcGxldGVWc05vbikgJT4lCiAgc3VtbWFyaXplKG1lYW5fbGFiX3ZhbHVlID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUKSkgJT4lCiAgZ2diYXJwbG90KHg9IkxhYkhvdXIiLCB5ID0gIm1lYW5fbGFiX3ZhbHVlIiwgZmlsbCA9ICJyZXBsZXRlVnNOb24iLCBmYWNldC5ieSA9ICJyZXBsZXRlVnNOb24iICkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMjQsMikpICArIHRoZW1lX3B1YnIoKQoKcmVwbGV0ZWRWc05vblJlcGxldGVkX1ByZVYgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShMYWJIb3VyID0gaG91cihjaGFydHRpbWUubGFiKSkgJT4lCiAgZmlsdGVyKHByZVZzUG9zdCA9PSAicHJlX3JlcGxldGlvbiIpICU+JQogIGdyb3VwX2J5KExhYkhvdXIsIHJlcGxldGVWc05vbikgJT4lCiAgc3VtbWFyaXplKG1lYW5fbGFiX3ZhbHVlID0gbWVhbih2YWx1ZW51bSwgbmEucm0gPSBUKSwgc3RkX2RldiA9IHNkKHZhbHVlbnVtLCBuYS5ybSA9IFQpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9TGFiSG91cix5PW1lYW5fbGFiX3ZhbHVlLGdyb3VwPXJlcGxldGVWc05vbiwgY29sb3IgPSByZXBsZXRlVnNOb24pKSArIGdlb21fbGluZSgpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwyNCwyKSkgICsgdGhlbWVfcHVicigpICsgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1tZWFuX2xhYl92YWx1ZS1zdGRfZGV2LCB5bWF4PW1lYW5fbGFiX3ZhbHVlK3N0ZF9kZXYpLCB3aWR0aD0uMiwKICAgICAgICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgwLjA1KSkKCgpyZXBsZXRlZFZzTm9uUmVwbGV0ZWRfUHJlViAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKExhYkhvdXIgPSBob3VyKGNoYXJ0dGltZS5sYWIpKSAlPiUKICBmaWx0ZXIocHJlVnNQb3N0ID09ICJwcmVfcmVwbGV0aW9uIikgJT4lCiAgZ3JvdXBfYnkoTGFiSG91ciwgcmVwbGV0ZVZzTm9uKSAlPiUKICBzdW1tYXJpemUobWVhbl9sYWJfdmFsdWUgPSBtZWFuKHZhbHVlbnVtLCBuYS5ybSA9IFQpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9TGFiSG91cix5PW1lYW5fbGFiX3ZhbHVlLGZpbGw9cmVwbGV0ZVZzTm9uKSkgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIiwgY29sb3IgPSAiYmxhY2siKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMjQsMikpICArIHRoZW1lX3B1YnIoKQpgYGAKCgo=